原文 : a word about volatile

Here’s my opening gambit: Volatile does not guarantee that variables are thread-safe, only that the most recent values are available to the thread.

Why do people always say that volatile is lightweight synchronized? Lightweight because it has less coding and running overhead and does not block, but at the cost of not having the full power of synchronized.

Noun explanation

  • Shared variables

If a variable has copies in the working memory of multiple threads, it is called a shared variable.

  • visibility

If changes made by one thread to a shared variable are visible to other threads in a timely manner, this is called shared variable visibility.

  • atomic

An operation or series of operations that cannot be interrupted.

Java memory model

Let’s start with a picture

  1. The Java memory model specifies that all variables are stored in main memory, and that each thread also has its own working memory, which holds a copy of the main memory of variables used by the thread.

  2. All reads and writes to variables by threads must be done in working memory, rather than directly reading or writing to variables in main memory, and different threads cannot access each other’s working memory.

  3. When a thread operates on a variable in its own working memory, it is flushed to main memory, and then another thread reads the variable in main memory again to get the latest value.

This results in some threads reading variables that are not up to date! So how does volatile solve this problem?

With the addition of volatile, a thread’s operation on a variable can be immediately fed back into main memory, where other threads sniff out the change, invalidate the working memory variable, and read it again from main memory when used, so that all other threads read the latest value.

For example,

visibility

public class Test {

    public boolean flag = false;

    public void waiting(a) {
        System.out.println("Waiting for the flag to change...");
        while(! flag) { } System.out.println("The flag has changed..." + flag);
    }
    
    public void change(a) {
        System.out.println("Change the flag");
        flag = true;
    }

    public static void main(String[] args) throws InterruptedException {
        Test test = new Test();
        new Thread(() -> test.waiting()).start();
        Thread.sleep(3000L);
        newThread(() -> test.change()).start(); }}Copy the code

Execute the above code and you will find that the first thread is always waiting, which conforms to the memory model above.

Then add flag to the keyword volatile and run it again. You will see that the thread ends as soon as flag is changed.

Thread unsafe condition

public class Test {

    public volatile int count = 0;

    public void change(a) {
        for (int i = 0; i < 100000; i++) { count++; }}public static void main(String[] args) throws InterruptedException {
        Test test = new Test();
        new Thread(() -> test.change()).start();
        new Thread(() -> test.change()).start();
        new Thread(() -> test.change()).start();
        Thread.sleep(2000L); System.out.println(test.count); }}Copy the code

Count is volatile, and we expect count to be 300,000 after three threads execute. But it turned out to be far less than 300,000.

Because volatile is atomic on a single read/write to the variable itself, count++ is not an atomic operation. It actually has three steps (read, modify, write), and problems can occur when multiple threads enter these three steps simultaneously.

But it also has thread-safe scenarios, so let’s talk about that.

Conditions of use

Volatile is not a substitute for synchronized, but can be used for thread safety under limited conditions where both conditions are met:

  1. Writing to a variable cannot depend on the value of the current variable. (example:count = count + 1)
  2. The current volatile variable is not contained in the invariants of other variables. (example:volatile1 < var2Constant)

Most programming scenarios conflict with one of these two conditions, which makes the use of volatile less common than synchronized.

This scenario is used in normal times

State marker quantity

We’ve already written that in visibility. A common feature of this class is that state markers are independent of other states in the program, and there is usually only one state transition.

Double checking

class Singleton{
    
    private volatile static Singleton instance = null;
     
    private Singleton(a) {}public static Singleton getInstance(a) {
        if(instance==null) {
            synchronized (Singleton.class) {
                if(instance==null)
                    instance = newSingleton(); }}returninstance; }}Copy the code

This class combined with synchronized implements the unique assignment of volatile variables, and when the assignment is successful other threads can immediately know the variable changes before acquiring the lock.

Review of previous articles

Never use arrays.aslist like this!

Eight diagrams to introduce you to Java

Java Development essentials manual

Attention wechat public number “code on actual combat” reply: interview video and architect send you very good information!