Author: Tangyuan

Personal blog: Javalover.cc

preface

Hello, officials, I am tangyuan, today to bring you is “object visibility – Volatile”, I hope to help, thank you

If the article is wrong, I hope you can point out, sincerely thank you

Introduction to the

When a thread modifies a shared variable (a non-local variable that all threads can access), the other threads can always read the latest value immediately, and the variable is said to be visible

If it’s a single thread, then the visibility is unquestionable and you can see it when you change it.

But with multiple threads, visibility can be maintained by means of locking or volatile modifiers.

PS: Actually, there is no real intestine. According to scientific studies, the human intestine is about 8 meters long (~ 5 times the height).

directory

  1. Visibility in single-thread versus multi-thread
  2. Volatile modifier
  3. Instruction reordering
  4. The difference between volatile and locking

The body of the

1. Visibility comparison between single-thread and multi-thread

So let’s take two examples here, to understand what visibility is, right

Here is a single threaded example with a shared variable

public class SignleThreadVisibilityDemo {
    // Share variables
    private int number;
    public void setNumber(int number){
        this.number = number;
    }
    public int getNumber(a){
        return this.number;
    }
    public static void main(String[] args) {
        SignleThreadVisibilityDemo demo = new SignleThreadVisibilityDemo();
        System.out.println(demo.getNumber());
        demo.setNumber(10); System.out.println(demo.getNumber()); }}Copy the code

As you can see, the first shared variable number is 0, but after calling setNumber(10), it reads 10

0
10
Copy the code

It would be nice if multithreading were as simple as that.

Now let’s look at an example of multithreading, again with shared variables

package com.jalon.concurrent.chapter3;

/** * <p> * Visibility: Multithreading visibility issues * </p> **@author: JavaLover
 * @time: 2021/4/27 * /
public class MultiThreadVisibilityDemo {
    // Share variables
    private int number;
    public static void main(String[] args) throws InterruptedException {
        MultiThreadVisibilityDemo demo = new MultiThreadVisibilityDemo();
        new Thread(()->{
          	// Here we make a false death loop, except for the assignment of number (except initialization)
            while (0==demo.number);
            System.out.println(demo.number);
        }).start();
        Thread.sleep(1000);
        // 168 is not a height, but an auspicious number
        demo.setNumber(168);
    }

    public int getNumber(a) {
        return number;
    }

    public void setNumber(int number) {
        this.number = number; }}Copy the code

The output is as follows:

Copy the code

The output is empty, and the program is still running.

This is where visibility issues arise, where the main thread changes the shared variable number and the child threads don’t see it

The reason?

Let’s use a picture. It’ll be easier

The steps are as follows:

  1. The child thread reads the number into its stack and backs it up
  2. The main thread reads number, modifies it, writes it, synchronizes it to memory
  3. The child thread is not aware of the number change at this time and still reads its own stack backup ready (possibly due to various performance optimizations).

So what’s the solution?

The lock or volatile modifier, in this case volatile

The modified code is as follows:

public class MultiThreadVisibilityDemo {
    Number is not backed up to other threads, but is stored in the shared heap
    private volatile int number;
    public static void main(String[] args) throws InterruptedException {
        MultiThreadVisibilityDemo demo = new MultiThreadVisibilityDemo();
        new Thread(()->{
            while (0==demo.number);
            System.out.println(demo.number);
        }).start();
        Thread.sleep(1000);
        // 168 is not a height, but an auspicious number
        demo.setNumber(168);
    }

    public int getNumber(a) {
        return number;
    }

    public void setNumber(int number) {
        this.number = number; }}Copy the code

The output is as follows:

168
Copy the code

As you can see, the child thread can see the changes made by the main thread as expected

Let’s explore the small world of Volatile

2. The volatile modifier

Volatile is a less efficient synchronization mechanism than locking. The main difference is that volatile does not guarantee atomicity, but it is lightweight

Let’s finish the example above;

We add the volatile modifier so that child threads can see the changes made by the main thread. What does volatile do?

In fact, we can think of volatile as a flag. If a virtual machine sees this flag, it will assume that the variable it is modifying is volatile, unstable, and can be modified by a thread at any time.

The virtual machine does not reorder the instructions associated with this variable (described below) and notifies threads of changes to this variable in real time (visibility)

This is what graphs look like:

As you can see, the backup of the number in the thread is no longer required. Every time a number is required, it is read directly from the heap, thus ensuring that the data is visible

3. Command reorder

Instruction reordering refers to the fact that the virtual machine sometimes changes the order in which instructions are executed to optimize performance, as long as the instruction dependencies are not broken (e.g. Int a = 10; int b = a; There is no reordering.)

Let’s take a look at the code that might be reordered:

public class ReorderDemo {
    public static void main(String[] args) {
        int a = 1;
        int b = 2;
        int m = a + b;
        int c = 1;
        int d = 2;
        intn = c - d; }}Copy the code

Here we want to understand a bottom knowledge, is the execution of each statement, in the bottom system is divided into several steps (such as the first step, the second step, the third step, etc., here we do not involve those assembly knowledge, you can refer to the “Actual Java high Concurrency” 1.5.4);

Now let’s go back to the above example, where the dependencies are as follows:

As you can see, they’re stacked in threes and threes, and they don’t depend on each other, and if they’re reordered, they’re going to look something like this

(The figure above is just a demonstration of the effect from the code level. In fact, the instruction reordering is much more detailed than this. Here we mainly understand the idea of reordering first.)

Since m=a+ B depends on the values of A and B, when the instruction is executed in the add part of m=a+b, if B is not ready, then M =a+ B needs to wait for B, and subsequent instructions will also wait.

But if you reorder and put m=a+b after it, then you can take advantage of the gap between adding and preparing for c and D;

This reduces wait times and improves performance (it feels a bit like school C, where you habitually define a bunch of variables before writing code).

4. Differences between volatile and locking

The difference between the following

lock volatile
atomic
visibility
order

The order mentioned above refers to the prohibition of instruction reordering, so that there will be no out-of-order problems in multithreading;

As we can see, the main difference between locking and volatile is atomicity;

This is mainly because volatile only modifiers on a variable, so it is somewhat like a compound operation on atomic variables. (Although atomic variables are atomic operations, multiple atomic variables are not guaranteed.)

conclusion

  1. Visibility is fine in a single thread, but multithreading can be problematic
  2. Volatile is a synchronization mechanism that is lighter than locking and ensures visibility and order of variables (prohibiting reordering)
  3. Instruction reordering: Sometimes a virtual machine will reorder code that does not depend on each other at runtime to optimize performance to reduce instruction wait time and improve efficiency
  4. The difference between locking and volatile: Atomicity is guaranteed by locking, not by volatile

Reference content:

  • Java Concurrent Programming
  • Real Java High Concurrency

Afterword.

And finally, thank you for watching. Thank you

Original is not easy, look forward to the three even yo