This is the fifth day of my participation in the August Wen Challenge.More challenges in August
Double-checked lock
public class UnsafeLazyInitialization {
private static Instance instance;
public static Instance getInstance(a) {
if (instance == null) // 1: thread A executes
instance = new Instance(); // 2: thread B executes
returninstance; }}Copy the code
public class SafeLazyInitialization {
private static Instance instance;
public synchronized static Instance getInstance(a) {
if (instance == null)
instance = new Instance();
returninstance; }}Copy the code
public class DoubleCheckedLocking { / / 1
private static Instance instance; / / 2
public static Instance getInstance(a) { / / 3
if (instance == null) { // 4: First check
synchronized (DoubleCheckedLocking.class) { / / 5: lock
if (instance == null) // 6: Second check
instance = new Instance(); // 7: The root of the problem is here
} / / 8
} / / 9
return instance; / / 10
} / / 11
}
Copy the code
Double-checked locking may seem perfect, but it’s a bad optimization! By the time the thread reaches line 4 and the code reads that Instance is not null, it is possible that the object referenced by instance has not been initialized.
The problem is step seven, which can be divided into three steps:
memory = allocate(); // 1: allocate the object’s memory space ctorInstance(memory); // 2: initializes the object instance = memory; // 3: Sets instance to the newly allocated memory address
2 and 3 can be reordered
This reordering can improve the performance of a single-threaded program without changing its execution results.
However, the reordering of A2 and A3 will cause thread B to determine at B1 that instance is not null, and thread B will next access the object referenced by instance. At this point, thread B will access an uninitialized object.
Solution:
1) Reordering of 2 and 3 is not allowed.
2) Allow 2 and 3 reorders, but do not allow other threads to “see” the reorder.
Volatile based solutions
public class SafeDoubleCheckedLocking {
private volatile static Instance instance;
public static Instance getInstance(a) {
if (instance == null) {
synchronized (SafeDoubleCheckedLocking.class) {
if (instance == null)
instance = new Instance(); // Instance is volatile}}returninstance; }}Copy the code
This scheme essentially ensures thread-safe delayed initialization by disallowing reordering between 2 and 3.
Class initialization-based solution
public class InstanceFactory {
private static class InstanceHolder {
public static Instance instance = new Instance();
}
public static Instance getInstance(a) {
return InstanceHolder.instance ; // This will cause the InstanceHolder class to be initialized}}Copy the code
The essence of this scheme is to allow reordering of 2 and 3 in 3 lines of pseudocode, but not to allow non-construction threads (thread B in this case) to “see” the reordering.
A class or interface type T is initialized immediately the first time any of the following occurs:
1) T is a class and an instance of type T is created.
2) T is a class, and a static method declared in T is called.
3) A static field declared in T is assigned.
4) A static field declared in T is used, and this field is not a constant field.
5) T is a Top Level Class (see §7.6 of the Java Language specification), and an assertion statement is executed nested within T.
In the InstanceFactory sample code, the thread that executes the getInstance() method for the first time causes the InstanceHolder class to be initialized (match 4).
The JVM acquires this initialization lock during class initialization, and each thread acquires the lock at least once to ensure that the class has been initialized
Lazy field initialization reduces the overhead of initializing a class or creating an instance, but increases the overhead of accessing a deferred initialized field.
In most cases, normal initialization is better than delayed initialization. If you do need thread-safe lazy initialization for instance fields, use the volatile based lazy initialization scheme described above.
If you do need thread-safe delay initialization for static fields, use the class-based initialization scheme described above