Introduction of 0.


Previous article ** Principles of Synchronized usage and the Process of optimizing and upgrading locksFrom the Angle of interview detailed analysis of the principle of synchronized keyword, this article mainly around the volatile keyword with code analysis of visibility, atomicity, order, synchronized also assist to prove, to deepen the understanding of lock.



1. The visibility


1.1 Invisibility

After thread A manipulates A shared variable, the shared variable is invisible to thread B. Let’s look at the following code.

package com.duyang.thread.basic.volatiletest;
/ * * *@author: jiaolian *@date: Created in 2020-12-22 10:10 *@description: Invisibility test *@modifiedBy: * Official account: lian */
public class VolatileTest {

    private static boolean flag = true;

    public static void main(String[] args) throws InterruptedException {
        Thread threadA = new Thread(() -> {
            while (flag){
                // Note that there is no output here
            };
            System.out.println("threadA over");
        });
        threadA.start();
        // Hibernate for 100 ms and let thread A execute first
        Thread.sleep(100);
        // The main thread sets the shared variable flag to false
        flag = false; }}Copy the code

In this code, thread A is started in the main thread, and the main thread sleeps for 100 milliseconds. The purpose is to let thread A execute first. The main thread finally sets the shared variable flag equal to false. As shown in the following figure, after the execution of the main thread, flag=false and the Java Memory model (JMM), the main thread sets the flag value of its working memory to false and synchronizes it to the main memory.Thread A did not read the latest flag value of main memory (false), the main thread is done, the thread A working memory has been occupying CPU time slice will not update the latest flag values from main memory, thread A * * can’t see the main memory of the latest value, A thread use value and the main thread using value, cause the program chaos, this is not visible between threads, so you should be able to see. ** Invisibility between threads is the root cause of the program’s loop.

1.2 Volatile Visibility

In the above example, we used code to prove that shared variables between threads are not visible. In fact, you can conclude from the figure above:As long asThread A’s working memory is aware of itIt would be nice to change the value of the shared flag variable in main memorySo that I can put the value of the latest update to A thread of working memory, as long as you can think of here, the problem ended, that’s right, the volatile keyword implements this function, thread A sense to the main memory Shared variables flag has changed, then forced to flag the latest value read from the main memory to work in his memory, so want toVolatileTestThe code terminates as normal, and the shared flag variable is volatile.private volatile static boolean flag = true; And you’re done. The underlying hardware basis for volatile implementation is based on hardware architecture and cache consistency protocols. If you want to dig deeper, check out the previous article **What is visibility? (Easy to understand)“. ** Must try to have a harvest oh!

1.3 Synchronized visibility

Synchronized ensures that shared variables are visible. Each time a lock is acquired, the latest shared variable is re-read from main memory.

package com.duyang.thread.basic.volatiletest;
/ * * *@author: jiaolian *@date: Created in 2020-12-22 10:10 *@description: Invisibility test *@modifiedBy: * Official account: lian */
public class VolatileTest {

    private static boolean flag = true;

    public static void main(String[] args) throws InterruptedException {
        Thread threadA = new Thread(() -> {
            while (flag){
                synchronized (VolatileTest.class){
                    
                }
            };
            System.out.println("threadA over");
        });
        threadA.start();
        // Hibernate for 100 ms and let thread A execute first
        Thread.sleep(100);
        // The main thread sets the shared variable flag to false
        flag = false; }}Copy the code

In the above code, I added A synchronization block to the while loop of thread A. Synchronized (volatiletest. class) locks the VolatileTest class. Finally, the program prints “threadA over”, and the program ends. It can be concluded that thread A will read the latest data of the main memory shared variable flag=false before locking. This proves that the synchronized keyword and volatile have the same visibility semantics.

2. The atomicity


2.1 atomic

Atomicity means that an operation either succeeds or fails as an indivisible whole.

2.2 Volatile non-atomicity

/ * * *@author: jiaolian *@date: Created in 2020-12-22 11:22 *@description: Atomicity test for the Volatile keyword *@modifiedBy: * Official account: lian */
public class VolatileAtomicTest {

    private volatile static int count = 0;

    public static void main(String[] args) throws InterruptedException {
        Task task = new Task();
        Thread threadA = new Thread(task);
        Thread threadB = new Thread(task);
        threadA.start();
        threadB.start();
        // The main thread waits for AB to complete!
        threadA.join();
        threadB.join();
        System.out.println("Accumulative count ="+count);
    }

    private static class Task implements Runnable {
        @Override
        public void run(a) {
            for(int i=0; i<10000; i++) { count++; }}}}Copy the code

In the above code, threads A and B are started in the main thread. Each thread increments the value of the shared variable count 10000 times. The console output below, which does not equal 20000, proves that volatile shared variables do not guarantee atomicity. The root cause of this problem is count++, which is not an atomic operation and is executed in the JVM in three steps.

  • Read the count value.
  • Add count by 1.
  • Writes the count value to main memory.

Thread-safety issues arise when multithreading counts ++.

2.3 Synchronized atomicity

Let’s modify the code above with the synchronized keyword.

/ * * *@author: jiaolian *@date: Created in 2020-12-22 11:22 *@description: Atomicity test for the Volatile keyword *@modifiedBy: * Official account: lian */
public class VolatileAtomicTest {

    private static int count = 0;

    public static void main(String[] args) throws InterruptedException {
        Task task = new Task();
        Thread threadA = new Thread(task);
        Thread threadB = new Thread(task);
        threadA.start();
        threadB.start();
        // The main thread waits for AB to complete!
        threadA.join();
        threadB.join();
        System.out.println("Accumulative count ="+count);
    }

    private static class Task implements Runnable {
        @Override
        public void run(a) {
            // This locks the Task instance, i.e. Task
            synchronized (this) {
                for(int i=0; i<10000; i++) {
                    count++;
                }
            }
        }
    }
}
Copy the code

Synchronized (this) locks the Task instance; synchronized (this) locks the Task instance. The execution sequence of threads A and B is synchronous, so the final result of AB thread running is 20000. The console output result is shown in the figure below.

3. The order


3.1 order

What is order?The Java code we write is not always executed sequentially, and there is always the possibility of program reordering (instruction reordering). The advantage of this is that the program code executing the block is executed first, and the program executing slowly is put behind, improving the overall efficiency of the operation. I’m going to draw a simple picture and then I’m going to give you an example code, and you’ll see.



As shown in the figure above, task 1 takes a long time and task 2 takes a short time. After JIT compilation, task 2 is executed first and then task 1, which has no impact on the final running results of the program
Improved efficiency
Ah (task 2 finishing first has no effect on results, but improves response time)!

/ * * *@author: jiaolian *@date: Created in 2020-12-22 15:09 *@description: Instruction rearrangement test *@modifiedBy: * Official account: lian */
public class CodeOrderTest {
    private static int x,y,a,b=0;
    private static int count = 0;
    public static void main(String[] args) throws InterruptedException {

        while (true) {
            // Initialize four variables
            x = 0;
            y = 0;
            a = 0;
            b = 0;
            Thread threadA = new Thread(new Runnable() {
                @Override
                public void run(a) {
                    a = 3; x = b; }}); Thread threadB =new Thread(new Runnable() {
                @Override
                public void run(a) {
                    b = 3; y = a; }}); threadA.start(); threadB.start(); threadA.join(); threadB.join(); count++;if (x == 0 && y==0) {
                System.out.println("Number of executions :"+count);
                break;
            } else {
                System.out.println("Number of executions :"+count+","+"x:"+x +" y:"+y); }}}}Copy the code

In the above code, the loop starts threads A and B, and if x and y are equal to 0, the program exits. Count is the program count. The following figure shows the printed part of the console program. It can be analyzed from the figure that when x and y are both equal to 0, A of thread A = 3; X = b; Two lines of code are reordered, thread B = 3; y = a; Two lines of code have also been reordered. This is what happens when the JIT compiler optimizes the code for reordering.

3.2 Volatile orderliness

A shared variable that is volatile is a barrier that prevents instructions from being reordered in three ways.

3.2.1 Instructions above the barrier can be reordered.

/ * * *@author: jiaolian *@date: Created in 2020-12-22 15:09 *@description: Instruction rearrangement test *@modifiedBy: * Official account: lian */
public class VolatileCodeOrderTest {
    private static int x,y,a,b=0;
    private static volatile int c = 0;
    private static volatile int d = 0;
    private static int count = 0;
    public static void main(String[] args) throws InterruptedException {

        while (true) {
            // Initialize four variables
            x = 0;
            y = 0;
            a = 0;
            b = 0;
            c = 0;
            d = 0;
            Thread threadA = new Thread(new Runnable() {
                @Override
                public void run(a) {
                    a = 3;
                    x = b;
                    c = 4; }}); Thread threadB =new Thread(new Runnable() {
                @Override
                public void run(a) {
                    b = 3;
                    y = a;
                    d = 4; }}); threadA.start(); threadB.start(); threadA.join(); threadB.join(); count++;if (x == 0 && y==0) {
                System.out.println("Number of executions :"+count);
                break;
            } else {
                System.out.println("Number of executions :"+count+","+"x:"+x +" y:"+y); }}}}Copy the code

In the above code, the loop starts threads A and B, and if x and y are equal to 0, the program exits. The shared variables C, d, are volatile modifications, and count is the program count counter. The following figure shows the printed part of the console program. It can be analyzed from the figure that when x and y are both equal to 0, A of thread A = 3; X = b; Two lines of code are reordered, thread B = 3; y = a; Two lines of code have also been reordered. It proves that the instructions above the barrier can be reordered.

3.2.2 Instructions below the barrier can be reordered.



As shown in the figure above, if the c and D barriers are placed above ordinary variables and the code is executed again, x and y will still be equal to 0 at the same time, proving that the instructions below the barriers can be rearranged.

3.2.3 Instructions above and below the barrier cannot be reordered.

/ * * *@author: jiaolian *@date: Created in 2020-12-22 15:09 *@description: Instruction rearrangement test *@modifiedBy: * Official account: lian */
public class VolatileCodeOrderTest {
    private static int x,y,a,b=0;
    private static volatile int c = 0;
    private static volatile int d = 0;
    private static int count = 0;
    public static void main(String[] args) throws InterruptedException {

        while (true) {
            // Initialize four variables
            x = 0;
            y = 0;
            a = 0;
            b = 0;
            c = 0;
            d = 0;
            Thread threadA = new Thread(new Runnable() {
                @Override
                public void run(a) {
                    a = 3;
                    // Forbid up and down rearrangements
                    c = 4; x = b; }}); Thread threadB =new Thread(new Runnable() {
                @Override
                public void run(a) {
                    b = 3;
                    // Forbid up and down rearrangements
                    d = 4; y = a; }}); threadA.start(); threadB.start(); threadA.join(); threadB.join(); count++;if (x == 0 && y==0) {
                System.out.println("Number of executions :"+count);
                break;
            } else {
                System.out.println("Number of executions :"+count+","+"x:"+x +" y:"+y); }}}}Copy the code

As in the above code, placing the barrier in the middle will prohibit the reordering of up and down instructions. The x and Y variables cannot be 0 at the same time, and the program will always be stuck in an endless loop, which proves that the code above and below the barrier cannot be reordered.

3.3 synchronized order

/ * * *@author: jiaolian *@date: Created in 2020-12-22 15:09 *@description: Instruction rearrangement test *@modifiedBy: * Official account: lian */
public class VolatileCodeOrderTest {
    private static int x,y,a,b=0;
    private static int count = 0;
    public static void main(String[] args) throws InterruptedException {

        while (true) {
            // Initialize four variables
            x = 0;
            y = 0;
            a = 0;
            b = 0;
            Thread threadA = new Thread(new Runnable() {
                @Override
                public void run(a) {
                    synchronized (VolatileCodeOrderTest.class) {
                        a = 3; x = b; }}}); Thread threadB =new Thread(new Runnable() {
                @Override
                public void run(a) {
                    synchronized (VolatileCodeOrderTest.class) {
                        b = 3; y = a; }}}); threadA.start(); threadB.start(); threadA.join(); threadB.join(); count++;if (x == 0 && y==0) {
                System.out.println("Number of executions :"+count);
                break;
            } else {
                System.out.println("Number of executions :"+count+","+"x:"+x +" y:"+y); }}}}Copy the code

In the above code, x and Y cannot be equal to 0 at the same time. The class object of the VolatileCodeOrderTest of synchronized lock, thread A and thread B are the same lock, and the code is executed synchronously and sequentially, so synchronized can guarantee the orderliness. Synchronized (this) indicates that the current thread (threadA or threadB) is not the same lock. If this is used, x and y are both equal to 0.

4. Learning methods for programmers


You can see I have a few articles analysis multithreaded spent a lot of energy is talking about visibility, atomicity, because these features are the basis of understanding multithreading, based and is particularly important, in my opinion, so how to write over and over again that I think too much, before that, there are many novice or have 2 to 3 years working experience of children’s shoes often ask me aboutJava learning methods, my advice to them is to lay a solid foundation, do not come up to learn advanced knowledge points or frameworks, such as ReentrantLock source code, Springboot framework, just like you play games, you play the difficulty level is relatively high, once the slope is relatively high you will be more uncomfortable and difficult, let alone facing the books, This is really the process from entry to quit. At the same time, don’t just think about it when you learn. It is not enough to think that you know this knowledge point by yourself. You need to write more codes and practice.Actually,There’s a lot of knowledge you seem to understand, but you don’t do it,You **** don’t really understand eitherI have been engaged in Java front-line research and development work for 7 years after graduation from the university, and I also led a team in the middle. Because I have gone through many twists and turns and stepped on holes, I still have some experience in learning programs. I will continue to sort out some experience and knowledge of the experience to share with you in the days to come, I hope you like to pay attention to me.I call drill, call a slogan start drill!

Summed up is two words: more hands-on, solid foundation* * * * * *. * * * * * *

5. To summarize


Today to talk with you about three important features of multithreading, with the code to achieve the way to elaborate on the meaning of these nouns, if the careful implementation of the code should be able to see it, like please click like and pay attention to oh. I am aCall lian [public number]“, while Shouting while practicing.