Singleton -DCL achieve double lock inspection and analysis of the principle plane

  In my experience, for example (if not please correct me), in the production process, often encounter the following two conditions:
  1. The package does not include a class class member variables with specific business meaning, is the business action package , such as the layers in MVC (HTTPRequest object is passed in a manner Threadlocal incoming).

  2. a class has global significance, once the object is instantiated as an object that can be used globally . As a class encapsulates the global location information and access information in a location method (without considering the explosion of the earth, plate movement), the information will not change and can be used globally.

  3. Many times the entire system only need to have a global object, this will help us conduct coordination of the overall system . The class is used to encapsulate the global configuration information.

  Under all three conditions are created if each time a new object, and the higher the frequency or volume when using larger class object, the object creation and GC frequent cause great waste of resources, while not conducive to the overall behavior of the system coordination. At this time, it needs to consider the use of a singleton object is to achieve the purpose of reuse.

  Before implementing Singleton pattern to see our first look at the four major principles for the use singleton Note:

  1. construct private . (Block class is instantiated by conventional means)

  2. static method or enumeration returned instance. (Ensure the uniqueness of instances)

  3. Make sure that only one instance, in particular, multi-threaded environment . (Thread-safe when you create instances)

  4. Ensure deserialization can not be reconstructed object. (Prevented sequence deserialization scene single embodiment inexplicable damaged, resulting not take into account the consequences)

  There are many ways to achieve Singleton pattern, we only discuss the most widely accepted degree of DCL way with a static inner classes manner (Benpian discussed DCL) .

  DCL (Double Check Lock) double checking locks

  We direct look at the code:

  

   We look directly at the core methods getCar (), we can see that if the judgment before the two new Car (), a sync block in the outside, in a sync block. We might as well simulate the process of creating a new instance:

  1. The first embodiment determines a single object reference points to null, if not null directly returns car, is null the process proceeds to the following sync block instantiate a new object.

  2. Get Car.class object object lock, in the case of multiple threads at the same time only one thread can acquire the lock and enter a synchronization block, executes the code sync blocks.

  3. Once sync blocks, where it is determined whether singleton object reference points to null. Here again the significance of the judgment is: While we were in before entering the synchronized block a judge, but in the process we are waiting to acquire the lock in some other thread has acquired the lock may have been carried out within the synchronized block content created for car examples. So we need to enter a synchronization block after a second judge, to determine if this car is still pointing to null, so this time we determined that only the current thread can be changed within a synchronized block car pointing, pointing at this time and car It is null. So at this point we can safely perform the instantiation of action.

  4. The current object thread instance completion and exit sync blocks, this time waiting for a lock position blocking the other thread (the first time has been determined if the determination result is true) acquires the lock and enter a synchronization block, if a second inspection and found that the car has been pointing an instance, not be instantiated action.

  As the Car class constructor we are rewritten as private methods , we can only get the instance (not just new in other classes) using the Car.getCar () method, thus ensuring the uniqueness of the instance. Once this is done, we guarantee the uniqueness obtain an instance of multi-threaded environment, but there is little not done: to ensure the uniqueness of the deserialized not destroy instances . When this point is often overlooked but very important, we use single-case model to design a class from the source instance of the global think there is only one class. But if the destruction of the examples in the serialization deserialization scene uniqueness, can lead to difficult to troubleshoot the error occurred . Although most cases, we will not let a single case of class implements Serializable, Externalizable interface, butin a complex inheritance we will inevitably not a single case of class inherited from ancestors realized Serializable, Externalizable interface.

  We follow readObject called when deserializing () source code look at the call stack:

private Object readOrdinaryObject(boolean unshared) throws 
 IOException {
        //此处省略部分代码
        Object obj;
        try {
            obj = desc.isInstantiable() ? desc.newInstance() : null;
        } catch (Exception ex) {
            throw (IOException) new InvalidClassException(
                desc.forClass().getName(),
                "unable to create instance").initCause(ex);
        }
        //此处省略部分代码
        if (obj != null &&handles.lookupException(passHandle) == null &&desc.hasReadResolveMethod()){
            Object rep = desc.invokeReadResolve(obj);
            if (unshared && rep.getClass().isArray()) {
                rep = cloneArray(rep);
            }
            if (rep != obj) {
                handles.setObject(passHandle, obj = rep);
            }
        }
        return obj;
    }

  We can see that the sixth line: obj = desc.isInstantiable () desc.newInstance ():? Null;

  If desc are serializable, then generate a new object to be invoked by reflection constructor with no arguments, there is provided a second new instances of the objects except for getCar () method entry , destroy the uniqueness of our example .

  We then look down, if (obj = null && handles.lookupException (passHandle) == null && desc.hasReadResolveMethod ()!) ---> If the method is instantiated object contains readResolve ();

  Object rep = desc.invokeReadResolve(obj);

  handles.setObject (passHandle, obj = rep); --------> readResolve call is instantiated object () generates a new object and returns.

  Then we rewrite readResolve () method which returns our unique instance, thus preventing damage to deserialize the singleton.

  

  Note that we did not use @Override to modify the method, because we do not know if realized in the inheritance chain Serializable class, Externalizable interface or temporarily not achieved but are not sure whether the future will be amended to implement the serialization interface. Realize they had rewritten when it is not achieved through this method to get the Heaven.

  Here feel has done a lot of work, but still missing a very important point: by the JVM instruction reordering caused famous DCL failures (JVM order execution function).

  Specific issues like this, JVM in order to optimize operating efficiency will be self-righteous about our instruction reordering or ignore the instructions it deems invalid (such as air circulation), to create an object is divided into the following three steps:

  1. In open heap memory space.
  2. In the heap memory instantiation of the parameters inside Car.
  3. The object pointing to the heap memory space.

  Wherein steps 2 and 3 may be executed out of order , if a case object thread instance in a sync block, the third step is performed (without the second step), while another thread is determined just cited point is null. Having executed three, so even if the object is not instantiated but will still be returned and used so abnormal. On the other hand, multi-core multithreading, multi-level due to CPU cache, change the variable for each thread of visibility can not be guaranteed. A thread has such an object is instantiated, but only a reference point and then perform multi-level cache A CPU thread, the not synchronized to the main memory. So for running other CPU thread is, car is still pointing to null!

  The official also found this problem in version 1.5 and fixes the problem, as long as the variable is declared as volatile, for before and after the reading and writing volatile variables are added memory barrier to prevent other instructions read and write operations before and after the reordering.

While also ensuring the visibility of the volatile multi-core multi-threaded environment variables for each CPU cache coherency between the various threads that is variable.

  volatile variable is that this variable may be changed unexpectedly , so the compiler would not have to assume that the value of this variable. In order to achieve volatile memory semantics, the compiler when generating bytecode will be inserted in the instruction sequence memory barrier to inhibit a particular type of processor reordering. But for the compiler, find an optimal arrangement to minimize the total number of insertion barrier is almost impossible, therefore, JMM adopted a conservative strategy.

  1. Write operation to insert a front barrier StoreStore each volatile.

  2. a write back operation is inserted in each barrier StoreLoad volatile.

  3. Insert a volatile LoadLoad barrier after each read operation.

  4. Insert a volatile LoadStore barrier after each read operation.

  volatile memory write barriers are respectively inserted in the front and rear, and two volatile reads a memory barrier is inserted at the back.

Memory barrier Explanation
StoreStore barrier General prohibition above and below volatile write write reordering.
StoreLoad barrier Prevent the above volatile write to the following may have volatile read / write reordering.
LoadLoad barrier Disables all ordinary read operation and the following volatile read the above reordering.
LoadStore barrier Ban all common writes and above the following volatile read reordering.

  When the non-volatile read and write variables, each thread start variable to the CPU memory cache copy . If the computer has multiple CPU, each thread can be processed on a different CPU, which means that each thread can be copied to a different CPU cache in. The declaration of a variable is volatile, the JVM to ensure that each read variables are read from memory, CPU cache skip this step .

  The above two points to ensure that the multi-core multithreading environment variables for each of the threads and the visibility of the variable ordering of read and write commands, so that a complete DCL singleton then finished, complete code is as follows:

Package Learning; 

Import the java.io.Serializable; 

public  class Car {
     // constructor is private, instantiating prohibited by conventional means 
    Private Car () {}
     // reference to a single embodiment object 
    static   volatile Car CAR = null ;
     // Get DCL singleton object 
    static Car getCar () {
         IF (CAR == null ) {
             the synchronized (Car. class ) {
                 IF (CAR == null ) { 
                    CAR = new new Car (); 
                } 
            } 
        }
        return car;
    }

    private Object readResolve() {
        return getCar();
    }
}

  We write a test case to see that running with three threads repeated calls getCar () method to obtain an instance, each get 20,000 times. Is not the only example of an exception is thrown if available. Here is the thread entity classes:

   

  The main function:

  

   Operating results, three threads are no exception is thrown, indicating that the way in the case of multiple threads is to ensure the correctness of a single case of:

  

  

  

  To summarize, the point to note when using DCL:

  1. volatile modified example references, cited guaranteed in the case of a multi-core multithreaded visibility of each thread, and reordering prohibition instruction is assigned by reference.

  2. The sentence should be carried out before and after entering the empty synchronized block. Before entering the logic required sentenced empty, waiting for the lock to prevent the process in the other thread changed the value of the first sentence quoted lead to empty space-time after entering an invalid sentence.

  3. Note that prevents damage to the deserialization of a single embodiment, achieved by rewriting readResolve () method.

  4. Define private constructors, other types of block instances created directly inlet.

 

END$$

Guess you like

Origin www.cnblogs.com/niuyourou/p/11891881.html