Learn about Volatile high concurrency keywords

Statement: respect other people's labor achievements. Please attach the original link to reprint! Learning and communication, for reference only!

1. What is volatile?

1. Volatile is a synchronization mechanism, which is lighter than synchronized and Lock, because using volatile does not cause expensive behaviors such as context switching.
2. If a variable is modified by volatile, it means that the JVM knows that the variable may be modified concurrently.
3. Volatile has very small overhead and corresponding functions. Although volatile is used for synchronization to ensure thread safety, volatile cannot do the atomic protection of synchronized. Volatile can only play a role in very limited scenarios.

2. Where is volatile practical?

2.1 not suitable for scenario a + + (increasing or decreasing, with dependencies)

public class Demo implements Runnable {
    volatile int a;
    public static void main(String[] args) throws InterruptedException {
        Demo demo = new Demo();
        // Two threads modify a shared variable concurrently
        Thread t1 = new Thread(demo);
        Thread t2 = new Thread(demo);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
         System.out.println("a="+demo.a);
    }
    @Override
    public void run() {
        for (int i = 0; i < 100000; i++) {
            a++;
        }
    }
}

result:

a=142019

If we don't look at the results first, our ideal situation should be a=200000, but why are the results so many times less?
Some people may say that it may be because the program has only been executed 142019 times. To verify this statement, I use the AtomicInteger class to make changes to the code.

public class Demo implements Runnable {
    AtomicInteger readA = new AtomicInteger();
    volatile int a;
    public static void main(String[] args) throws InterruptedException {
        Demo demo = new Demo();
        Thread t1 = new Thread(demo);
        Thread t2 = new Thread(demo);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("a=" + demo.a);
        System.out.println("readA="+demo.readA.get());
    }
    @Override
    public void run() {
        for (int i = 0; i < 100000; i++) {
            a++;
            readA.incrementAndGet();
        }
    }
}

result:

a=193825
readA=200000

Seeing the results, the program did run 200000 times, but why did a + + run so many times less?

   because we all know that a + +, in fact, it contains three independent operations. Read the value of a, add 1 to the value, and then write the calculation result to A. This is a "read modify write" operation sequence. If two threads perform an increment operation on a counter at the same time without synchronization, there will be a value deviation.

2.2 suitable scenarios

2.2.1 assignment

   if a shared variable is only assigned by each thread from beginning to end without other operations, volatile can be used instead of synchronized or atomic variable, because the assignment itself is atomic and volatile ensures visibility, so it is enough to ensure thread safety.
For example, two threads assign values to the shared variable a

public class Demo {
    volatile int a;
    public void change1() {
        a = 1;
        System.out.println(a);
    }
    public void change2() {
        a = 2;
        System.out.println(a);
    }
    public static void main(String[] args) throws InterruptedException {
        Demo demo = new Demo();
        new Thread(new Runnable() {
            @Override
            public void run() {
                demo.change1();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                demo.change2();
            }
        }).start();
    }
}
2.2.2 trigger

  you may be familiar with triggers. You have heard of triggers in MySql, but the triggers mentioned here are used as triggers for variables before refreshing.
The following pseudo code also illustrates the scenario of volatile as a trigger
After performing A series of configuration operations, thread A assigns A value of true to the variable initialized modified by volatile
Thread B will sleep all the time when it judges that initialized is false. It will not go through the following code until initialized is true, and use some configurations initialized by the thread. At this time, the variable initialized modified by volatile is used as the trigger

3. Two functions of volatile?

1. Visibility: before each thread reads a volatile variable, it needs to invalidate the corresponding local cache, then read the latest value of the variable from the main memory, and then copy it to a separate workspace. After modifying the value of the variable, it will be immediately brushed into the main memory.
2. Prohibition of instruction reordering Optimization: Although reordering is beneficial, prohibition of instruction reordering can solve the problem of single case double lock disorder.

4. volatile summary

In this respect, volatile can be regarded as a lightweight version of synchronized. If a shared variable is only assigned by each thread from beginning to end without other operations, volatile can be used to replace synchronized or atomic variables. Because the assignment itself is atomic and volatile ensures visibility, it is enough to ensure thread safety.

The attribute read and write operations of volatile attribute (if only multiple threads assign values to a shared variable, this can be ignored) are all lock free operations. It cannot replace synchronized because it is not atomic and mutually exclusive. Because there is no lock, there is no need to spend time on locking and releasing the lock, so it is low-cost.

Volatile can only be used in attributes. If volatile is used to modify attributes, the compiler will not reorder the attributes.

volatile also has visibility (if any thread changes the value of a shared variable, other threads can see it immediately), and also provides the guarantee of happens before (the previous write operation and the next read operation can all know what you have done).

Keywords: Java

Added by patrick87 on Thu, 28 Oct 2021 08:47:33 +0300