Talk about the understanding of the Volatile keyword in Java

  • 2020-05-09 18:31:14
  • OfStack

The keyword volatile may have been heard and used by many of you. Before Java 5, it was a controversial keyword because its use in programs often led to unexpected results. After Java 5, the volatile keyword was brought back to life. The volatile keyword, while literally simple, is not an easy thing to use.

1. Introduction

JMM provides the volatile variable definition, final, synchronized blocks to ensure visibility.
With the variable modified by volatile, the thread reads the most modified value of the variable every time it USES it. volatile is easily misused for atomic operations. I've written a couple of examples of tests that you can try out 1.

2. The main program


public class Main{
public static void main(String[] args) throws InterruptedException{
List<Thread> threadList = new ArrayList<Thread>();
for(int i=0; i<10; ++i){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Single.Holder.instance.add();
}
});
threadList.add(thread);
thread.start();
}
for(Thread thread : threadList)
thread.join();
System.out.println(Single.Holder.instance.x);
}
}

3. Singleton mode test

1. No volatile, no synchronized


class Single{
public int x = 0;
public void add(){
try {
TimeUnit.MILLISECONDS.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
++this.x;
}
public static class Holder{
public static Single instance = new Single();
}
} 

Output result: 8, 9, 10 have appeared. You can run more, try one more, and you'll find different results.

2. It has volatile, but not synchronized


class Single{
public volatile int x = 0;
public void add(){
try {
TimeUnit.MILLISECONDS.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
++this.x;
}
public static class Holder{
public static Single instance = new Single();
}
}

Output: 9 and 10 appear at most.

3. There is no volatile, there is synchronized


class Single{
public int x = 0;
public synchronized void add(){
try {
TimeUnit.MILLISECONDS.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
++this.x;
}
public static class Holder{
public static Single instance = new Single();
}
}

Output: 10 no matter how many times you run it.

4. About the application of volatile in DCL(double check lock)


public class LazySingleton {
private int someField;
private static LazySingleton instance;
private LazySingleton() {
this.someField = new Random().nextInt(200)+1; // (1)
}
public static LazySingleton getInstance() {
if (instance == null) { // (2)
synchronized(LazySingleton.class) { // (3)
if (instance == null) { // (4)
instance = new LazySingleton(); // (5)
}
}
}
return instance; // (6)
}
public int getSomeField() {
return this.someField; // (7)
}
}

First of all, let me explain why this does not work in java!

Assume that thread Ⅰ is first invoked getInstance () method, and then thread Ⅱ also call getInstance () method and getSomeField () method, we to thread Ⅰ statement (1) not happen - before thread Ⅱ statement (7). Thread Ⅱ getInstance the execution () method statement (2), because the access to instance not in sync block, so the thread Ⅱ observed may also look for may not be the thread Ⅰ in statement (5) to write instance, that is, the value may be null or instance is not empty. We first assume that instance value is not empty, also was observed thread Ⅰ to write instance, then thread Ⅱ statements should be executed (6) returns the value of the instance directly, and then to the getSomeField instance call () method, this method is also without any synchronization is called, so that the whole thread Ⅱ operations are cut with without synchronization, it shows that thread Ⅰ statements (1) and thread Ⅱ statements (7) does not exist between happen - before relationship, This means that the thread Ⅱ in executing statements (7) can't see the thread Ⅰ in statement (1) the someFiled write values, this is DCL problem. Ridiculous, right? DCL had been intended to flee in sync, it achieved this goal, it is because of this, it eventually punished, such program does exist serious bug, although the bug was found that the probability of absolute is much lower than the probability of winning the lottery, and it is fleeting, is more terrible, even if you wouldn't think happened is caused by DCL.

My understanding is: thread and thread I II has his own work storage, thread I created after instance to memory refresh time is uncertain, so the thread Ⅱ in executing statements (7) can't see the thread Ⅰ of someFiled written in sentence (1) values.

So since java 5 has added an extra rule of happen-before:

The & # 8226; Write operations on the volatile field happen-before subsequent read operations on the same field.

With this rule we can declare instance as volatile, private volatile static LazySingleton instance;
According to this rule, we can get, thread Ⅰ statement (5) > Language thread Ⅱ sentence (2) (thread), according to the rules of the single thread, thread Ⅰ statements (1) - > Thread thread Ⅱ Ⅰ statement (5) and language (2) - the sentence > Language thread Ⅱ sentence (7), and then according to the rules of transfer has thread Ⅰ statements (1) - > Thread Ⅱ sentence (7), which means that the thread Ⅱ can observe thread Ⅰ in statement (1) when writing values of someFiled program will be able to get the correct behavior.

Note: before java5, the synchronization semantics of the final field are no different from other variables. In java5, when the final variable 1 is set in the constructor (as long as the this reference is not disclosed in the constructor), other threads must see the value set in the constructor. The problem with DCL is that we see the default values for the member variables of the object, so we can set LazySingleton's someField variable to final, so that it will work correctly in java5.

The above content is the site for you to introduce the Java Volatile keyword knowledge, I hope to help you!


Related articles: