What is a publish object?
Publishing an object means making an object usable by code outside the current scope
What is object escape?
Object escape is a false release, which means that when an object has not been constructed, it is visible to other threads
escape-demo
@ Slf4j public class Escape { private int thisCanBeEscape = 0; public Escape() { new InnerClass(); } private class InnerClass{ public InnerClass() { log.info("{}", Escape.this.thisCanBeEscape); } } public static void main(String[] args) { new Escape(); } }
In this instance, the Escape object has not been constructed, and the member variable thisCanBeEscape of the object is accessed. This class is thread-unsafe, and it is not recommended to write in this way.
Insecure release - demo
@ Slf4j public class UnsafePublish { private String[] states = {"a", "b", "c"}; public String[] getStates() { return states; } public static void main(String[] args) { UnsafePublish unsafePublish = new UnsafePublish(); log.info("{}", Arrays.toString(unsafePublish.getStates())); unsafePublish.getStates()[0] = "d"; log.info("{}", Arrays.toString(unsafePublish.getStates())); } }
The output is: [a,b,c] and [d,b,c] Objects published in this way are thread-unsafe, because there is no guarantee that other threads will modify the states field, resulting in a state error
Four ways to safely publish objects:
a. Initialize an object reference in a static initialization function
b. Save the reference of the object to the volatile type field or the AtomicReference object
c. Save a reference to the object in a final type field of a properly constructed object
d. Save a reference to the object in a field protected by a lock
How to safely publish an object?
We use the implementation of different singletons to illustrate the method of safely publishing objects
singleton-demo1
public class SingletonExample1 { //private constructor private SingletonExample1() { } // singleton object private static SingletonExample1 instance = null; // static factory method public static SingletonExample1 getInstance() { //There is no problem with single thread, there will be problems when multi-threaded if (instance == null) { instance = new SingletonExample1(); } return instance; } }
There is no problem with this instance in single-threaded mode, but in multi-threaded mode, if two threads run at the same time to judge instance==null, it is possible to create two new instances, so this is thread unsafe Yes, this is also the lazy mode. This instance satisfies the a condition. If you add the d condition and lock it when judging whether it is null, it can become thread-safe.
singleton-demo2
public class SingletonExample2 { //The hungry mode is insufficient. If there is too much processing in the construction method, it will cause the class to load very slowly. //private constructor private SingletonExample2() { } // singleton object private static SingletonExample2 instance = new SingletonExample2(); // static factory method public static SingletonExample2 getInstance() { return instance; } }
This demo is thread-safe. Two points need to be paid attention to when using the hungry mode. One is that this class must be used (to avoid waste of resources), and the other is that there is not too much processing in the private construction method. 4
singleton-demo3
public class SingletonExample3 { //private constructor private SingletonExample3() { } // singleton object private static SingletonExample3 instance = null; // static factory method public static synchronized SingletonExample3 getInstance() { //There is no problem with single thread, there will be problems when multi-threaded if (instance == null) { instance = new SingletonExample3(); } return instance; } }
This demo is the case where demo1 is locked, which is thread-safe, but it is not recommended to write in this way, because although thread-safety is guaranteed, there is a certain overhead in performance.
singleton-demo4
/** * Lazy mode double synchronization lock singleton mode * The singleton instance is created when it is used for the first time * The double detection mechanism is not necessarily thread-safe because of the existence of instruction reordering */ public class SingletonExample4 { //private constructor private SingletonExample4() { } // singleton object private static SingletonExample4 instance = null; // static factory method public static SingletonExample4 getInstance() { //There is no problem with single thread, there will be problems when multi-threaded if (instance == null) { //Double detection mechanism //sync lock synchronized (SingletonExample4.class) { if (instance == null) { instance = new SingletonExample4(); } } } return instance; } }
This demo is an optimized version of lazy mode, but note that this demo is not thread-safe because there is instruction rearrangement. When instance=new SingletonExample4() is executed, the cpu will perform three steps: 1. memory=allocate() Allocate the memory space of the object 2, ctorInstance() initializes the object 3, instance = memory Set instance to point to the memory just allocated, but due to the optimization of jvm and cpu, instruction rearrangement will occur, such as the result of rearrangement becomes 1, 3, 2. There is no problem in the case of single thread, but problems may occur in the case of multi-threading. If thread A executes to instance=new SingletonExample4() at this time, instruction rearrangement occurs, and the second step is executed. 3. At this time, instance has executed the memory of the object, but the object has not been initialized. If thread B happens to execute if (instance==null) at this time, the condition is no longer established, and it returns directly because this object It has not been initialized, and problems may occur when using this object directly.
Singleton-demo5
/** * Lazy mode double synchronization lock singleton mode * The singleton instance is created when it is used for the first time * The double detection mechanism is not necessarily thread-safe because of the existence of instruction reordering */ public class SingletonExample5 { //private constructor private SingletonExample5() { } //1, memory = allocate() allocates the memory space of the object // 2. ctorInstance() initializes the object // 3. instance = memeory set instance to point to the memory just allocated //Restrict instruction rearrangement through volatile and double detection mechanism, volatile restricts the write operation of code //Singleton object, instruction rearrangement occurs by volatile restriction code private volatile static SingletonExample5 instance = null; // static factory method public static SingletonExample5 getInstance() { //There is no problem with single thread, there will be problems when multi-threaded if (instance == null) { //Double detection mechanism //sync lock synchronized (SingletonExample5.class) { if (instance == null) { instance = new SingletonExample5(); } } } return instance; } }
This demo is an upgraded version of demo4. As long as the problem of instruction rearrangement is solved, in the previous blog "Thread Safety", we have introduced that volatile can limit the code to have instruction rearrangement. This demo is thread-safe.
singleton-demo6
/** * Hungry mode * Singleton instance is created at class loading */ @ThreadSafe public class SingletonExample6 { //The hungry mode is insufficient. If there is too much processing in the construction method, it will cause the class to load very slowly. //private constructor private SingletonExample6() { } //Initialize the static field of the singleton object private static SingletonExample6 instance = null; // static block method static { instance = new SingletonExample6(); } // static factory method public static SingletonExample6 getInstance() { return instance; } public static void main(String[] args) { System.out.println(getInstance().hashCode()); System.out.println(getInstance().hashCode()); } }
demo2 is the static code field method of the hungry man mode, this demo is the static code block method of the hungry man mode, this demo is also thread-safe
singleton-demo7
/** * Enumeration mode: safest * It is easier to ensure security than lazy mode * Compared with the hungry mode, the initial initialization is only done when the actual call is made */ public class SingletonExample7 { private SingletonExample7() { } // static factory method public static SingletonExample7 getInstance() { return Singleton.INSTANCE.getSingleton(); } private enum Singleton{ INSTANCE; private SingletonExample7 singleton; //JVM guarantees that this method is called absolutely only once, and is initialized before this class is called Singleton() { singleton = new SingletonExample7(); } public SingletonExample7 getSingleton() { return singleton; } } }
This demo is our most recommended singleton writing method, and it is thread-safe. It is easier to guarantee in terms of security than the lazy mode. Compared with the hungry mode, the initial initialization is only done when the actual call is made.