JAVA memory model
The notes made here are combined with the java memory model in the JVM
Java memory model in the art of concurrent programming with java
Combine some interview questions
There is a clear difference between the JVM memory area and the JAVA memory model
To distinguish the relationship between them
JMM is a rule, mainly to study the visibility of concurrent multi-threaded memory
It is a process abstraction for cache to read and write access
The purpose is to ensure that the memory should beConsistent memoryof
Main memory and working memory
The Java memory model creates a working memory for each thread
But all variables should be stored in the main memory, which is not the memory on the hardware.
But part of the JVM
Volatile's memory semantics
Semantics 1 Modifications to Volatile variables are immediately visible to other threads
But calculations using Volatile variablesNotThread-safe
For example, if multithreading increments a volatile variable without locking it, a synchronization error will occur
because
Num ++ is actually composed of 4 bytecode operations. The num at the top of the stack is likely to be increased by other threads.
problem solved:
1. You can use AtomicInteger increment operation is incrementAndGet () method
2.方法synchronized
scenes to be used:
1. The operation result does not depend on the current value, or single-thread changes such as the set get method do not depend on the original value
2. Variables do not require other threads to participate in variable constraints
Semantic 2 Volatile prohibits instruction reordering optimization
volatitle boolean config = false ;
finishconfig(config);
config = true;
while(!config){
sleep();
}
dosomethings();
If config is not a Volatile variable
config may be faster than fhinishconfig
This may cause others to execute before configuration.
Summarize the three characteristics of volatile variables
The reading and writing itself are atomic, the others are not self-increasingAtomicity
The operation of the Volatile variable is visible to all threads, and the cache is forced to refresh Visibility
Volatile variables cannot be reordered to add memory barriers Orderly
Lock memory semantics
The release and acquisition of locks is essentially message notification
When a thread releases a lock, it tells the next to acquire the lock message of the thread.
When a thread acquires a lock, it actually receives a lock message from a thread.
sychronized is essentially similar to locking and lock method
The operations in sychronized are atomic because only one thread executesAtomicity
After the sychronized operation is visible to all threads, unlocking a variable will synchronize the variable to the main memoryVisibility
The code in sychronized cannot be reordered because only one thread is executing Orderly
The implementation of some locks is essentially to modify the Volatile variable for CAS operation to achieve memory consistency
Memory semantics of final fields
final is different from lock and Volatile
Final field access is similar to ordinary variables
But the final field must obey two reordering rules
- The writing of the final field in the constructor and the subsequent reference of this object are assigned to a reference variable. This operation cannot be reordered
- Reading a reference to a final field for the first time, and subsequently reading this final field, this operation cannot be reordered
// 一个对象
static Ex object;
// 构造函数
public Ex(){
// i 是 final域
i=0;
// j 是普通域
j=2;
}
// A线程写
public void write(){
object = new Ex();
}
// B 线程读
public void read(){
Ex obj = object ;
int a = obj.i;
int b = obj.j;
}
Look at the example above
The B thread must read out obj.i, the final field, after the initialization of the constructor.
For obj.j, which is a common field, the initial value is not necessarily read, but may be the default initial value.
The final field guarantees that the value or reference of the object must be properly initialized
The final field guarantees that the reference of the object must be read before the final field of the object is read
happens-before
The antecedent rule is an important principle for judging whether data is competing and whether the thread is safe.
- The program sequence is executed in sequence flow within a single thread
- Tube lock (monitor lock) unlocks a lock before the lock is locked
- The volatile principle writes the Volatile variable before reading the variable
- Thread start principle The thread start method start () is executed before all operations in the thread
- Thread termination principle All operations of the thread are executed before the thread termination, such as the Thread.join end method
- The principle of thread interruption is that the interrupted () operation of the thread precedes the detection of interrupt events in the interrupted thread
- Object finalization principle The construction method of an object precedes its finalize method
- Transitivity, A is prior to B, B is prior to C, then A is prior to C
Double check lock DCL
Double check lock referred to as DCL is a very common lazy loading technology
That is, for some objects with large overhead, they are loaded when they are used. Load on demand. Lazy loading
The following is a thread-unsafe initialization object
public class LazyInital {
class Instatnce{
}
private static Instatnce instatnce;
public Instatnce getInstatnce(){
if(instatnce==null) // Thread A
instatnce = new Instatnce(); // Thread B
return instatnce;
}
}
A simple change is to add the sychronized logo to the getInstance method.
But this performance is not good
Below is the wrong version of DCL
public Instatnce getInstatnce(){
if(instatnce==null){
synchronized (LazyInital.class){
if(instatnce==null)
instatnce = new Instatnce();
}
}
return instatnce;
}
This is called double check lock and it looks very comfortable but has big problems
The initialization of java object is divided into 3 parts
- memory = allocate () // allocate memory
- initclass (memory) // initialize object
- instance = memory // point to object
2 and 3 may be reordered.
The last situation may be
Thread A has not initialized the instance. Thread B has already obtained a reference to the object. At this time, it returns an uninitialized object.
Solution
- Disallow initialization of objects and reordering of pointed objects
Use Volatile's semantics to
add the Volatile description
- The initialization of thread A objects can be reordered, but should not be visible to thread B.
Use class initialization solution, rely on the characteristics of JVM initialization class object lock
public class InstanceFactory {
static class Instance{
}
// instanceHolder类的初始化加锁
private static class InstanceHolder{
private static Instance instance = new Instance();
}
public static Instance getInstance(){
return InstanceHolder.instance;
}
}
The difference between volatile and synchronized
-
The essence of volatile is to tell jvm that the value of the current variable in the working memory is uncertain, and it must be read from the main memory
Synchronized locks the current variable, which can only be read by one thread, and other threads block until the lock is released.
-
Volatile variable level, synchronized method variable
-
Volatile can only achieve the visibility of variable modifications, and cannot guarantee the atomicity of operations. For example, Volatile's reading and writing are atomic, but Volatile's operations are not necessarily atomic and self-increasing.
-
Volatile will not block, synchronized will block
-
volatitle variable will not be optimized, synchronized will be optimized