Java-Detailed explanation of volatile keywords

Introduction to volatile

Overview of volatile

  1. volatile is a 比synchronizedkeyword 更轻量级, 同步机制and the operation will be performed when accessing the volatile variable 加锁, so it will be executed 线程阻塞.
  2. volatile assurance 可见性and 禁止指令重排序, through the bottom is " 内存屏障to achieve", but 不保证原子性.
  3. 写入volatile variable is equivalent to 退出同步代码块, 读取volatile variable equivalent 进入同步代码块.

volatile usage scenarios

  1. The input operation of the variable 不依赖变量的当前值may ensure 只有单个线程更新the value of the variable;
  2. The variable 不会与其他状态变量一起纳入不变性条件中; (The variable is not included in the invariant of other variables)
  3. When accessing variables 不需要加锁.

Use Cases

  1. 状态标记量: According to the status flag, terminate the thread. That is, volatile can be used in scenarios where a certain status flag is checked to determine whether to exit the loop.
  2. 单例模式中的double check: Volatile modify instance because instance = new Singleton()this line of code is not an atomic operation, allocate memory to instance, call Singleton's constructor to initialize member variables, and point the instance object to the allocated memory space

JMM's special rules for the definition of volatile variables:

  1. The current thread 使用变量前都must be 先从主内存刷新最新的值used every time to ensure that you can see the modification of variables by other threads. ( read、load、use连续执行, From memory to 工作memory.)
  2. Currently 修改变量后, you must go 立刻同步to the main memory every time to ensure that other threads can see their modifications to the thread. ( assign、store、writeContinuous execution, from 工作memory to memory.)
  3. Variables modified by volatile 不会被指令重排序优化ensure that the execution order of the code is the same as the order of the program.

Visibility && Principle of prohibiting instruction reordering optimization

(Realized by the underlying lock instruction)
  volatile can guarantee thread visibility and provide a certain order, but can not guarantee atomicity, volatile is implemented by " 内存屏障" in the bottom layer of JVM . Two layers of semantics: 保证可见性,不保证原子性; 禁止指令重排序.

Visibility

  Ensure the visibility of volatile variables to all threads . Visibility means that when a thread modifies the value of this variable, the new value is immediately known to other threads. Ordinary variables are not allowed. The transfer of values ​​of common variables between threads needs to be done through main memory. For example, thread A modifies the value of a common variable and writes back to main memory. Another thread B writes after thread A finishes writing Only after reading from the main memory, the new variable value is visible to thread B.
  The volatile bottom layer uses the lock prefix to cause the Cache of this CPU to be written into memory . This writing action will also cause other CPUs or other cores to invalidate its Cache , which is equivalent to JMM's " store and write " of variables in the Cache. "Operation, this thread invalidates the cache line of the variable of other threads . When other threads need to read the variable and find that the variable cache line is invalid , the data is reloaded from the main memory , so the visibility of the data is guaranteed .
  Writing volatile variables is equivalent to exiting the synchronization code block , and reading volatile variables is equivalent to entering the synchronization code block . The locking mechanism can ensure both visibility and atomicity, but volatile variables only guarantee visibility, not atomicity .

Prohibit instruction reordering optimization

  When the variable is declared as volatile, the compiler and runtime will notice that the variable is shared , because the operation on the variable will not be reordered with other memory operations, and the volatile variable will not be cached in the register or other Where the processor is not visible.
  Variables modified by volatile will perform an additional lock operation in the assembly code , which is equivalent to a memory barrier (Memory Barrier, which means that the subsequent instructions cannot be reordered to the position before the memory barrier when reordering ).
  There is no need for a memory barrier when only one CPU accesses memory; but if multiple CPUs access the same memory, and one of them is observing the other, a memory barrier is needed to ensure consistency. When the lock instruction synchronizes the modification to the memory, it means that all previous operations have been performed, which can form a feeling that the instruction reordering cannot cross the memory barrier .
The principle of volatile prohibiting instruction reordering:
Insert picture description here

  1. When 第二个操作是volatile写the time, no matter what the first operation is 都不能重排序. This rule ensures that operations before volatile writes will not be reordered by the compiler after volatile writes.
  2. When 第一个操作是volatile读the time, no matter what the second action is 都不能重排序. This rule ensures that operations after volatile reads will not be reordered by the compiler before volatile reads.
  3. When 第一个操作是volatile写, 第二个操作是volatile读when,不能重排序 .

Q&A

What is the reason for as-if-serial semantics to allow reordering of operations that have control dependencies?

  In a single-threaded program, reordering operations that have control dependencies will not change the execution result; but in a multithreaded program, reordering operations that have control dependencies may change the execution results of the program.

What is the principle of happen-before?

  1. Sequential execution of code rules : in the same thread, the previous operation happens-before subsequent operations;
  2. Add unlock rule : the 解锁operation on the monitor happens-before its subsequent 加锁operation;
  3. Volatile rule : volatilethe operation of variable happens-before subsequent operation;
  4. Thread startup rules : thread start()method happens-before all subsequent operations of the thread;
  5. Thread terminates rules : Thread All operations happen-before another thread calls on that thread join()returns after the success of the operation;
  6. Thread interruption rules : interrupt()the call to the thread method happens-before the interrupted code detects the occurrence of an interruption event, and the Thread.interrupted()method can detect whether an interruption occurs.
  7. Object termination rules : the beginning of a 对象的初始化完成happen-before finalize()method (end of constructor execution) .
  8. Transitivity : If a happen-before b, b happen-before c, then a happen-before c.

What is the difference between locking and volatile?

Similarities :
  writing volatile variables is equivalent to exiting the synchronization code block , and reading volatile variables is equivalent to entering the synchronization code block .
Differences: The
  locking mechanism can ensure both visibility and atomicity ; but volatile variables only guarantee visibility , not atomicity .

What is the difference between ordinary variables and volatile variables?

  volatileThe special rules 保证新值能立即同步to 主内存, each 使用前capable of immediately 从主内存刷新. So volatile can guarantee the 多线程variable 可见性while operating 普通变量不保证.

  1. A volatile modified variable can guarantee "visibility" : when a thread modifies the value of this variable, the new value is immediately known to other threads.
  2. Ordinary variables cannot guarantee "visibility" : the transfer of values ​​of common variables between threads requires main memory to complete. Thread A modifies the value of a common variable, first locks to read from main memory, and then writes back to main memory (Lock, read, load, use, assign, store, write, unlock). Another thread B will read the latest value (lock, read, load, use, unlock) from the main memory after the write back of thread A is completed (unlocked), and the new variable value will be visible to thread B.

What is the semantic meaning of atomicity?

volatile不保证原子性, But talk about atomic semantics.
1) is defined atom:
i.e. a plurality of operations or operations 要么全部执行and performed 过程不会by any factor 打断, 要么都不执行.
2) Example:

i = 2;          
i = j ;           
i++;           
i = j + 2;

In Java, to ensure that only the basic data types of variables and assignment operations are atomic operations (if it is in the 32-bit JDK environment for long和doublethe 64位数据of the 读取不是原子operation, will be divided into high and low 两次32位操作.) May be a single thread, as atomic entire procedure Sexuality, but multithreading needs to ensure atomicity through synchronized and locks.
i = 2The operation is 原子的only involved in assigning basic data type 2 to i;
i = j, yes 两个操作, take the value of j first, and then assign the value of j to i , 非原子操作;
i ++, yes 三个操作, take the value of i first, and then increment the value of i, Finally, assign the incremented result to i, yes 非原子操作;
i = j + 2, yes 三个操作, take the value of j first, then j + 2, and finally assign the result of j + 2 to i, yes 非原子操作;

Before JDK1.5, why Java cannot safely use DCL (double lock detection) to implement singleton mode?

  The semantics of the reordering of volatile masking instructions were only completely fixed in JDK1.5. The code before and after the volatile variable still has reordering issues.
1) DCL singleton mode :

public class Singleton {

	// volatile修饰变量
	private volatile static Singleton instance;
	
	private Singleton(){}
	
	// 单例方法
	public static Singleton getInstance(){
		if(instance == null){
			// 加锁synchronized代码块
			synchronized (Singleton.class){
				if(instance == null){
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
	public static void main(String[] args) {
		Singleton.getInstance();
	}    
}

2) Use static inner classes to implement a safer mechanism

/* 静态内部类 */
public class SingletonInner { 

    // 静态内部类
	private static class Holder { 
		private static SingletonInner singleton = new SingletonInner();
	 } 
	 
	private SingletonInner(){} 
	
	public static SingletonInner getSingleton(){ 
		return Holder.singleton; 
	}
 }
Published 118 original articles · Like 150 · Visits 40,000+

Guess you like

Origin blog.csdn.net/Andya_net/article/details/105566755