1 JMM
1. Understanding of Volatile
Volatile is a lightweight synchronization mechanism provided by Java virtual machine
1. Ensure visibility
2. Atomicity is not guaranteed
3. Prohibit instruction rearrangement
How to achieve visibility
The shared variable modified by volatile variable returns one more line of assembly when writing:
0x01a3de1d:movb $0×0,0×1104800(%esi);0x01a3de24**:lock** addl $0×0,(%esp);
Instructions prefixed with Lock cause two things in multi-core processors.
1) Writes the data of the current processor cache line back to the system memory.
2) This write back to memory operation will invalidate the data cached in the memory address in other CPUs.
2 what is JMM
JMM: JAVA Memory Model, which does not exist, is a concept and an agreement!
Some synchronization conventions of JMM:
1. Before the thread is unlocked, the shared variables must be immediately flushed back to main memory;
2. Before locking the thread, the latest value in the main memory must be read into the working memory;
3. Locking and unlocking are the same lock;
Threads are divided into working memory and main memory
8 operations:
Read: acts on the main memory variable, which transfers the value of a variable from the main memory to the working memory of the thread for subsequent load action;
load: the variable that acts on the working memory. It puts the read operation from the main memory into the working memory;
Use: it acts on the variables in the working memory. It transfers the variables in the working memory to the execution engine. Whenever the virtual machine encounters a value that needs to be used, it will use this instruction;
assign: a variable that acts on the working memory. It puts a value received from the execution engine into the variable copy of the working memory;
store: a variable that acts on the main memory. It transfers the value of a variable from the working memory to the main memory for subsequent write;
write: a variable that acts on the main memory. It puts the value of the variable obtained from the working memory by the store operation into the variable of the main memory;
lock: a variable that acts on the main memory and identifies a variable as a thread exclusive state;
The main variable can be unlocked only after it is unlocked by the thread;
JMM provides corresponding regulations for these eight operations:
One of the read and load, store and write operations is not allowed to appear separately. That is, read must be loaded and store must be written
The thread is not allowed to discard its latest assign operation, that is, after the data of the work variable is changed, it must inform the main memory
A thread is not allowed to synchronize data without assign from working memory back to main memory
A new variable must be born in main memory. Working memory is not allowed to directly use an uninitialized variable. That is, before the use and store operations on variables, they must go through the assign and load operations
Only one thread can lock a variable at a time. After multiple locks, you must perform the same number of unlocks to unlock
If you lock a variable, it will clear the value of this variable in all working memory. Before the execution engine uses this variable, you must re load or assign to initialize the value of the variable
If a variable is not locked, it cannot be unlocked. You cannot unlock a variable that is locked by another thread
Before unlock ing a variable, it must be synchronized back to main memory
import java.util.concurrent.TimeUnit; public class Test { // If volatile is not added, the program will loop // Adding volatile ensures visibility // private volatile static Integer number = 0; private static Integer number = 0; public static void main(String[] args) { //main thread //Child thread 1 new Thread(()->{ while (number==0){ } }).start(); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } number=1; System.out.println(number); } }
Encountered a problem: the program does not know that the value in main memory has been modified
2 volatile
1. Ensure visibility
Adding volatile ensures visibility
import java.util.concurrent.TimeUnit; public class Test { // If volatile is not added, the program will loop // Adding volatile ensures visibility private volatile static Integer number = 0; public static void main(String[] args) { //main thread //Child thread 1 new Thread(()->{ while (number==0){ } }).start(); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } number=1; System.out.println(number); } }
2. Atomicity is not guaranteed
Atomicity: indivisible;
Thread A cannot be disturbed or divided when executing tasks. It either succeeds or fails at the same time.
/** * Atomicity is not guaranteed * number <=2w * */ public class Test { private static int number = 0; public static void add(){ number++; //++Not an atomic operation, but two ~3 operations } public static void main(String[] args) { //Theoretically, number = = = 20000 for (int i = 1; i <= 20; i++) { new Thread(()->{ for (int j = 1; j <= 1000 ; j++) { add(); } }).start(); } while (Thread.activeCount()>2){ //main gc Thread.yield(); } System.out.println(Thread.currentThread().getName()+",num="+number); } }
main,num=19885
How to ensure atomicity without lock and synchronized?
Using atomic classes to solve problems
import java.util.concurrent.atomic.AtomicInteger; /** * Atomicity is not guaranteed * number <=2w * */ public class Test { private static volatile AtomicInteger number = new AtomicInteger(); public static void add(){ // number++; //++Not an atomic operation, but two ~3 operations number.getAndIncrement(); //atomicInteger +1 method cas } public static void main(String[] args) { //Theoretically, number = = = 20000 for (int i = 1; i <= 20; i++) { new Thread(()->{ for (int j = 1; j <= 1000 ; j++) { add(); } }).start(); } while (Thread.activeCount()>2){ //main gc Thread.yield(); } System.out.println(Thread.currentThread().getName()+",num="+number); } }
main,num=20000
The bottom of these classes are directly linked to the operating system! Is to modify the value in memory.
Unsafe class is a very special existence;
Why are atomic classes so advanced?
3 prohibit instruction rearrangement
What is instruction rearrangement?
The programs we write are not executed by the computer as we write
Source code – > compiler optimization rearrangement – > instruction parallelism may also rearrange – > memory system may also rearrange – > execution
When the processor rearranges instructions, it will consider the dependence between data!
int x=1; //1 int y=2; //2 x=x+5; //3 y=x*x; //4 //The order of execution we expect is 1_ 2_ 3_ 4 the possible execution sequence will become 2134 1324 //Could it be 4123? impossible
It may appear in thread A, first execute b=1, and then execute x=a;
It may occur in the B thread. First execute a=2, and then execute y=b;
Then the possible results are as follows: x=2; y=1.
volatile avoids instruction rearrangement:
volatile will add a memory barrier, which can ensure the order of instructions in this barrier.
Memory barrier: CPU instruction. effect:
1. Ensure the execution sequence of specific operations;
2. Memory visibility of some variables can be guaranteed (with these features, the visibility of volatile implementation can be guaranteed)
summary
volatile can ensure visibility but not atomicity. Due to memory barrier, it can avoid instruction rearrangement
Interviewer: so do you know where to use this memory barrier most? Singleton mode