JMM
The Java Memory Model (JMM) is an abstract concept that describes a set of rules or specifications used to solve the problem of multi-threaded shared variables
Memory swap operation
Reference: www.jianshu.com/p/4cfd09fe2…
- Lock: A variable that acts on main memory, marking a variable as thread-exclusive
- Unlock: A variable that acts on main memory. It releases a locked variable so that it can be locked by another thread
- Read: Acts on a main memory variable that transfers the value of a variable from main memory to the thread’s working memory for subsequent load action
- Load: Variable acting on working memory, which puts a read operation variable from main memory into working memory
- Use: Applies to variables in working memory. It transfers variables in working memory to the execution engine. This instruction is used whenever the virtual machine reaches a value that requires the variable to be used
- Assign: A variable applied to working memory that places a value received from the execution engine into a copy of the variable in working memory
- Store: Acts on a variable in main memory. It transfers a value from a variable in working memory to main memory for subsequent writes
- Write: A variable in main memory that puts the value of a variable in main memory from the store operation in working memory
The JMM lays down the following rules for the use of these eight directives:
- One of the read and load, store and write operations is not allowed to occur separately. If you use read, you must load; if you use store, you must write
- A thread is not allowed to discard its most recent assign operation, in which it must inform main memory that the work variable’s data has changed
- A thread is not allowed to synchronize unassigned data from working memory back to main memory
- A new variable must be created in main memory. Working memory is not allowed to use an uninitialized variable directly. Before performing use and store operations on variables, you must pass assign and load operations
- Only one thread can lock a variable at a time. You must unlock the device for the same number of times
- If a variable is locked, all of its values in working memory will be emptied. Before the execution engine can use the variable, the variable must be reloaded or assigned to initialize its value
- You cannot unlock a variable if it is not locked. You cannot unlock a variable that is locked by another thread
- Before an UNLOCK operation can be performed on a variable, it must be synchronized back to main memory
The problem
When thread B changes flag to flase and writes it back to main memory, thread A still uses flag=true.
Volatile
Volatile is a lightweight synchronization mechanism provided by the JVM
- Guaranteed visibility
- Atomicity is not guaranteed
- Disallow command reordering
visibility
public class Test {
private static int num=0;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while(num==0) {}; }).start(); TimeUnit.SECONDS.sleep(2);
num=1; }}Copy the code
Because num=1 is not caught by the thread, the program will not stop
The solution is simply to add the volatile keyword
public class Test {
private volatile static int num=0;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while(num==0) {}; }).start(); TimeUnit.SECONDS.sleep(2);
num=1; }}Copy the code
Atomicity is not guaranteed
Num is not 20000;
public class Test {
private volatile static int num=0;
public static void add(a){
num++;// Not an atomic operation
}
public static void main(String[] args) throws InterruptedException {
for (int i=0; i<20; i++){new Thread(()->{
for (int j = 0; j < 1000; j++) { add(); }}).start(); } System.out.println(num); }}Copy the code
What guarantees atomicity other than lock and synchronized
// private static AtomicInteger num=new AtomicInteger(0);
public class Test {
private static AtomicInteger num=new AtomicInteger(0);
public static void add(a){
num.getAndIncrement();
}
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while(num.get()==0) {}; }).start(); TimeUnit.SECONDS.sleep(2);
for (int i=0; i<20; i++){new Thread(()->{
for (int j = 0; j < 1000; j++) {
add();
}}).start();
}
TimeUnit.SECONDS.sleep(10); System.out.println(num); }}Copy the code
Java. Util. Concurrent. Atomic – atomic operation type package provides a set of atomic variable classes in it. Private volatile int value; Visibility is guaranteed.
Instruction rearrangement
The sequence of instructions that a computer executes after being compiled by a program compiler. Generally, this sequence of instructions produces a definite result. To ensure that each execution has a definite result. But, in general, the CPU and the compiler to enhance the efficiency of program execution, according to certain rules to allow instruction optimization, in some cases, this optimization will bring some logic problems of execution, the main reason is that the code is there between the logical order, in the case of concurrent execution, can produce ambiguity, namely according to the different execution logic, You’ll get different results.