JAVA Concurrent Programming - Volatile Keyword and Memory Visibility

Posted by Davidc316 on Sun, 28 Jul 2019 07:06:06 +0200

Author: All his life
Wechat: 878799579

1. What is JUC?

JUC full name java.util.concurrent is a very common utility class in concurrent programming.

2.Volatile keywords

1. If a variable is modified by volatile keyword, it is visible to all threads.
2. If a thread modifies the variable value modified by Volatile, the modified value is immediately visible to other threads.
3. Variables that are not Volatile-modified are safe under multithreading
4. SynchronousQueue or Exchanger can be used to transfer data between threads

3. Memory visibility

Memory Visibility means that when one thread is using the object state and another thread is modifying the state at the same time, it is necessary to ensure that when one thread modifies the object state, other threads can see the state changes that occur.
Visibility errors refer to the fact that when read and write operations are performed in different threads, we cannot ensure that the thread performing the read operation can see the values written by other threads in time, sometimes even impossible.
The principle is the same as CAS principle. Students who don't understand it can Baidu by themselves. Attached is a CAS demonstration map for your reference.

4. Examples of actual combat

Threads are used to modify the value of the variable count, and the results are compared using the Volatile keyword modifier and not using the Volatile modifier.

First, let's look at the implementation of Runnable through internal classes. The variable < font color= "red"> modifies the demonstration and results with the Volatile keyword </font>.

package org.bilaisheng.juc;

/**
 * @Author: bilaisheng
 * @Wechat: 878799579
 * @Date: 2019/1/1 16:29
 * @Todo: Runnable is implemented through internal classes and variables are decorated with Volatile keywords
 * @Version : JDK11 , IDEA2018
 */
public class NoVolatileTest{

    public static void main(String[] args) {
        NoVolatileThread noVolatileThread = new NoVolatileThread();
        new Thread(noVolatileThread).start();

        while (true){
            if(noVolatileThread.isFlag()){
                System.out.println("flag At this point true !");
                break;
            }
        }
    }
}

class NoVolatileThread implements Runnable{

    private boolean flag = false;
    
    @Override
    public void run() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        flag = true;

        System.out.println(Thread.currentThread().getName() + " flag = " + flag);
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

The results of the operation are as follows:

Next, let's look at the implementation of Runnable through internal classes, where the variable < font color= "red"> does not use the Volatile keyword </font> to modify the presentation and the results.

package org.bilaisheng.juc;

/**
 * @Author: bilaisheng
 * @Wechat: 878799579
 * @Date: 2019/1/1 16:53
 * @Todo: Runnable is implemented through internal classes and variables are decorated with Volatile keywords
 * @Version : JDK11 , IDEA2018
 */
public class VolatileTest{

    public static void main(String[] args) {
        VolatileThread volatileThread = new VolatileThread();
        new Thread(volatileThread).start();

        while (true){
            // if's judgment of volatile ensures that it was correct at the time, then thread a may be dormant.
            // Thread b also judges that it does not exist, and thread b has a new one.
            // Then a thread wake up and execute new volatile as needed to get the latest value.
            if(volatileThread.isFlag()){
                System.out.println("flag At this point true !");
                break;
            }
        }
    }
}

class VolatileThread implements Runnable{

    private volatile boolean flag = false;

    @Override
    public void run() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        flag = true;

        System.out.println(Thread.currentThread().getName() + " flag = " + flag);
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

The results of the operation are as follows:

By comparison, we find that there are some deviations in the output of variables modified by Volatile and not by Volatile. Why on earth?

Step by step, we disassemble the above code execution steps:

1. For modifying variables without using Volatile keywords:

  • Step 1: Default flag = false;
  • Step 2 The main thread's cache area does not refresh the flag value. So flag is still false. So no output < flag is true at this time! >
  • Step 3: Subthread output Thread-0 flag = true

2. For modifying variables with Volatile keywords:

  • Step 1: Default flag = false;
  • Step 2: The main thread sees that flag is a variable modified by the Volatile keyword. Gets the latest flag variable value, where flag = true. So output < flag is true at this time! >
  • Step 3: Subthread output Thread-0 flag = true

5. Advantages of Volatile

Visibility: Variables modified by Volatile refresh the values in main memory immediately to ensure that other threads can get the latest values when they get them. All threads see the same value of the variable.

Lightweight synchronized, high concurrency guarantees variable visibility.

6. Disadvantages of Volatile

1. Frequent refresh of variables in main memory may cause performance bottlenecks

2. It does not have atomicity of operation, so it is not suitable to write the variable depending on the variable itself. i++, for example, does not guarantee atomicity through volatile.

7. Pay attention to me if you like.

Topics: Java Programming