Two methods to solve the memory visibility problem in Java multithreading: synchronized, volatile and CAS algorithm


Preface

First, we briefly understand Java's memory model, and then lead to memory visibility issues based on a piece of code.

  • Java memory model The
    Java memory model stipulates that all variables are stored in the main memory. Each thread has its own working memory. The working memory of the thread stores the variables used by the thread (these variables are from the main memory Copied from). All operations (reading, assignment) of variables by threads must be performed in working memory.Different threads cannot directly access the variables in each other's working memory, and the transfer of variable values ​​between threads needs to be completed through the main memory .

  • Code example

public class 内存可见性问题 {
    
    
    public static void main(String[] args) {
    
    
        MyRunable0 myRunable0 = new MyRunable0();
        Thread th = new Thread(myRunable0);
        th.start();
        while (true){
    
    
            //程序没有进入,线程在其工作内存中将共享变量值修改为true,但是没能及时刷新到主存中
            if(myRunable0.isFlag()){
    
    
                System.out.println("进来了");
                break;
            }
        }

    }
}
class MyRunable0 implements Runnable{
    
    
    boolean flag=false;

    public boolean isFlag() {
    
    
        return flag;
    }

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

    @Override
    public void run() {
    
    
        flag=true;
        System.out.println("线程将flag改成"+flag);

    }
}
  • operation result
    Insert picture description here
  • Visibility
    The modification of a shared variable value by a thread can be seen by other threads in time.
  • Atomicity
    Atomicity means that the operation is indivisible. Whether it is multi-core or single-core, an atomic quantity, only one thread can operate on it at a time.
  • Achieve visible conditions
  1. The shared variable value modified by the thread can be refreshed from the working memory to the main memory in time
  2. Other threads can update the latest value of shared variables from main memory to their own working memory in time
  • Implementation of Visibility Supported at the Java Language Level
  1. synchronized
  2. volatile

One, synchronized to achieve visibility

1.synchronized can be achieved

  • Atomicity (synchronization)
  • Visibility

2. Two regulations of JVM on synchronized

  • Before the thread is unlocked, the latest value of the shared variable must be flushed to the main memory.
  • When the thread is locked, the value of the shared variable in the working memory will be cleared, so when using the shared variable, the latest value must be re-read from the main memory.

3. Code

        while (true){
    
    
        //使用 synchronized实现可见性,注意锁的使用
           synchronized (myRunable0){
    
    
               if (myRunable0.isFlag()) {
    
    
                   System.out.println("进来了");
                   break;
               }
           }
        }

4. Disadvantages of synchronized

  • Unable to control the actual length of the block
  • Blocking cannot be interrupted
  • Low efficiency

Two, volatile achieves visibility

1. Volatile can be achieved

  • Visibility issues
  • No guarantee of atomicity

2. Implementation principle

It is achieved by adding memory barriers and prohibiting reordering optimization.

  • When performing a write operation to a volatile variable, a store barrier instruction will be added after the write operation
  • When performing a read operation on a volatile variable, a load barrier instruction is added before the read operation

In layman's terms, every time a volatile variable is accessed by a thread, the value of the variable is forced to be re-read from the main memory. When the variable changes, it will force the thread to refresh the latest value to the main memory. In this way, at any time, different threads can always see the latest value of the variable.

3. Code

		//在共享变量前添加volatile关键字
 volatile boolean flag=false;

4. Applicable scenarios of volatile

  • The write operation to the variable does not depend on its current value
  • The variable is not included in an invariant with other variables

3. CAS (Compare-And-Swep) algorithm

1 Overview

  • CAS, Compare and Swap compare and exchange.There are a total of three operands, a memory value v, a thread local memory old value a (the value before the desired operation) and a new value b. During the operation, compare the old value a and the memory value v for changes. If there is no change, the memory value v can be updated to the new value b. If there is a change, it will not be exchanged.

2. Code

public class CAS算法 {
    
    
    public static void main(String[] args) {
    
    
        Mrunable mrunable = new Mrunable();
        for (int i = 0; i < 10; i++) {
    
    
            new Thread(mrunable).start();
        }

    }
}
class Mrunable implements Runnable{
    
    
    //Java提供好的原子变量
    AtomicInteger i=new AtomicInteger(1);
    
    public AtomicInteger getI() {
    
    
        return i;
    }

    @Override
    public void run() {
    
    
        while (true){
    
    
            try {
    
    
                Thread.sleep(50);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            //使用原子变量调用相应的方法完成需求
            System.out.println(i.getAndIncrement());
        }


    }
}

to sum up

Synchronized and volatile comparison

  • Volatile does not require locks, is more lightweight than synchronized, and does not block threads
  • From the perspective of memory visibility, volatile read operations are equivalent to locking, and volatile write operations are equivalent to unlocking.
  • Synchronized can guarantee both visibility and atomicity, while volatile can only guarantee visibility and cannot guarantee atomicity.

Guess you like

Origin blog.csdn.net/m0_46988935/article/details/112937371