Java single-instance mode depth resolution

Source: Internet
Author: User

Application Scenarios

Because singleton mode generates only one instance, it reduces system performance overhead (e.g., when an object is generated that requires more resources, such as reading a configuration and generating other dependent objects, it can be resolved by producing a singleton object directly at application startup and then permanently residing in memory)

    • Task Manager in Windows;
    • File system, an operating system can only have one file system;
    • The design and implementation of database connection pool;
    • In spring, there is only one instance of a component Java-web, a servlet class has only one instance;

Implementation Essentials

    • Declare as private to hide the constructor
    • private static Singleton instance
    • Declared as public to expose instance fetching methods

The single-case mode mainly pursues three aspects performance

    • Thread Safety
    • High call efficiency
    • Lazy Loading
Implementation method

There are five main implementations, lazy (lazy loading, initialization on use), a Hungry man (declaration-time initialization), double check, static inner class, enumeration.

Lazy, thread-insecure implementations

Because there is no synchronization, multiple threads may simultaneously detect that the instance has not been initialized and initialize separately, thereby destroying the singleton constraint.

publicclass Singleton {    privatestatic Singleton instance;    privateSingleton() {    };    publicstaticgetInstance() {        ifnull) {            new Singleton();        }        return instance;    }}  
Lazy, thread-safe but inefficient implementations

Since objects need to be synchronized only at initial initialization, in most cases there is no need for mutually exclusive access to objects, and locks can cause significant resource consumption

publicclass Singleton {    privatestatic Singleton instance;    privateSingleton() {    };    publicstaticsynchronizedgetInstance() {        ifnull) {            new Singleton();        }        return instance;    }}  
Double check

This method is compared to the above method to ensure that only when the initialization needs synchronization, when the initialization is complete, call getinstance again will not enter the synchronized block.
NOTE

Internal checks are necessary.

Because there may be multiple threads in an if statement outside the synchronization block that simultaneously detects that instance is null and wants to acquire a lock, it is also necessary to determine whether or not to null after entering the synchronization block, to avoid the subsequent thread that acquires the lock to initialize the instance again

The instance declaration is necessary for volatile types.
    • Command rearrangement
      Because the initialization operation Instance=new Singleton () is non-atomic, it consists of three main processes
      1. allocating memory to instance
      2. Call Constructor Initialize instance
      3. Instance points to allocated space (instance points to allocated space, instance is not empty)
        Although the synchronized block guarantees that only one thread is entering the synchronization block, the JVM inside the synchronization block may have instructions reflow for optimization purposes, for example (1->3->2), Instance is not initialized before other threads will check externally to instance not NULL, and return instance that have not yet been initialized, resulting in a logic error.
        • The visibility of volatile guaranteed variables
          The volatile type variable guarantees the visibility of the write to the read, and the JVM does not reorder operations on the volatile variable with other memory operations, and the volatile variable is not cached in the register. This ensures that the current state of the instance is always detected when detecting the instance state.

Note: Volatile does not guarantee the atomicity of the operation, for example, even if count is declared as a volatile type, the count++ operation is decomposed to read-write two operations, although reading to the latest value of count, However, there is no guarantee that no other thread will write again between reads and writes, resulting in a logic error

 Public  class Singleton {    Private Static volatileSingleton instance;Private Singleton() {    }; Public StaticSingletongetinstance() {if(Instance = =NULL) {synchronized(Singleton.class) {if(Instance = =NULL) {instance =NewSingleton (); }            }        }returnInstance }}
A Hungry man type

This approach is based on a single Classloder mechanism, instance initialization at class loading and avoids synchronization problems. The advantage of a hungry man is that it is easy to achieve, disadvantage is not lazy loading mode (lazy initialization)

    • Initialization is completed before an instance is required, and in the case of more than one case, memory consumption and slow loading are caused.
    • Because initialization is done before calling getinstance (), if you need to pass in parameters to the getinstance () function, you will not be able to implement
publicclass Singleton {    privatestaticfinalnew Singleton();    privateSingleton() {    };    publicstaticgetInstance() {        return instance;    }}  
Static Inner class

Because the inner class is not used outside the class, it is only loaded when the getinstance () method is called. The ClassLoader class loading mechanism, which relies on the JVM, ensures that no synchronization problems occur.

publicclass Singleton {    privateSingleton() {    };    publicstaticgetInstance() {        return Holder.instance;    }    privatestaticclass Holder{        privatestaticnew Singleton();    }}  
Enumeration methods

See Enumeration class resolution
-Thread Safety
Because the enumeration class compiles to a class that inherits from Java.lang.Enum at compile time, its constructor is private, the enumeration object can no longer be created, the Declaration and initialization of the enumeration object are in the static block, so the security of the thread is ensured by the JVM's classloader mechanism. but lazy loading is not possible
-Serialization
Because the enumeration type uses a special serialization method, it guarantees that there can be only one instance in a JVM.

    • An instance of an enumeration class is static and exists in an array, which can be obtained using the values () method
    • When serializing, only the name attribute that represents the enumeration type is output name
    • When deserializing, the corresponding enumeration object is found in a static array according to the name, because no new objects are created, thus guaranteeing that there is only one object in the JVM
publicenum Singleton {    INSTANCE;    publicerror(){        return"error";    
Damage and defensive reflection of a single case model

For enumeration classes, the crack method does not apply.

ImportJava.lang.reflect.Constructor; Public  class TestCase {     Public void Testbreak()throwsException {class<singleton> clazz = (class<singleton>) class.forname ("Singleton");        constructor<singleton> Constructor = Clazz.getdeclaredconstructor (); Constructor.setaccessible (true);        Singleton Instance1 = Constructor.newinstance ();        Singleton Instance2 = Constructor.newinstance (); System.out.println ("Singleton?"+ (Instance1 = = Instance2)); } Public Static void Main(string[] args)throwsexception{NewTestCase (). Testbreak (); }}
Serialization of

for enumeration classes, the crack method does not apply.
The test first needs to declare Singleton to implement the Serializable interfacepublic class Singleton implements Serializable

 Public  class TestCase {    Private Static FinalString System_file ="Save.txt"; Public void Testbreak()throwsException {Singleton Instance1 = singleton.getinstance (); ObjectOutputStream Oos =NewObjectOutputStream (NewFileOutputStream (System_file));        Oos.writeobject (Instance1); ObjectInputStream Ois =NewObjectInputStream (NewFileInputStream (System_file));        Singleton Instance2 = (Singleton) ois.readobject (); System.out.println ("Singleton?"+ (Instance1 = = Instance2)); } Public Static void Main(string[] args)throwsexception{NewTestCase (). Testbreak (); }}
ClassLoader

There are two types of classloader in the JVM, the boot inside loader (bootstrap) and the user-defined loader (user-defined class loader), where there may be multiple classloader in one JVM, Each classloader has its own namespace. A classloader can have only one instance of a class object type, but different classloader may have the same class object instance, which can cause fatal problems.

Defense

For serialization and deserialization, we need to add a custom deserialization method so that it no longer creates an object, but returns an existing instance directly, guaranteeing a singleton pattern.
We test it again with the following class, and we find that the result is true.

 Public Final  class Singleton {    Private Singleton() {    }Private Static FinalSingleton INSTANCE =NewSingleton (); Public StaticSingletongetinstance() {returnINSTANCE; }PrivateObjectReadresolve()throwsobjectstreamexception {//Instead of the object we ' re on,        //Return the class variable INSTANCE        returnINSTANCE; } Public  class TestCase {    Private Static FinalString System_file ="Save.txt"; Public void Testbreak()throwsException {Singleton Instance1 = singleton.getinstance (); ObjectOutputStream Oos =NewObjectOutputStream (NewFileOutputStream (System_file));        Oos.writeobject (Instance1); ObjectInputStream Ois =NewObjectInputStream (NewFileInputStream (System_file));        Singleton Instance2 = (Singleton) ois.readobject (); System.out.println ("Singleton?"+ (Instance1 = = Instance2)); } Public Static void Main(string[] args)throwsException {NewTestCase (). Testbreak (); }}  }
A summary of the performance of single-case mode
Way Advantages Disadvantages
A Hungry man type Thread-safe, high-efficiency calls Cannot delay loading
Lazy type Thread-safe, can delay loading The call is inefficient.
Double detection lock type Thread-safe, high-efficiency calls that can delay loading -
Static internal class type Thread-safe, high-efficiency calls that can delay loading -
Enumeration single Example Thread-safe, high-efficiency calls Cannot delay loading
Single-instance performance test

Test results:

    1. Hungersingleton Total time: 30 ms
    2. Lazysingleton Total time: 48 ms
    3. Doublechecksingleton Total Time: 25 ms
    4. Staticinnersingleton Total Time: 16 ms
    5. Enumsingleton Total time: 6 ms

The enumeration type gets the best efficiency without considering lazy loading, and the lazy mode has the lowest efficiency because each method needs to acquire the lock, so the static inner class is similar to the double-check effect. Enumeration is a good way to implement a singleton pattern, considering that enumerations can easily and effectively avoid serialization and reflection.

 Public  class TestCase {    Private Static FinalString System_file ="Save.txt";Private Static Final intThread_count =Ten;Private Static Final intCircle_count =100000; Public void testsingletonperformance()throwsIOException, Interruptedexception {FinalCountdownlatch latch =NewCountdownlatch (Thread_count); FileWriter writer =NewFileWriter (NewFile (System_file),true);LongStart = System.currenttimemillis (); for(inti =0; i < Thread_count; ++i) {NewThread (NewRunnable () {@Override                 Public void Run() { for(inti =0; i < Circle_count;                    ++i) {Object instance = singleton.getinstance ();                } latch.countdown ();        }}). Start (); } latch.await ();LongEnd = System.currenttimemillis (); Writer.append ("Singleton Total time:"+ (End-start) +"Ms \ n");    Writer.close (); } Public Static void Main(string[] args)throwsexception{NewTestCase (). Testsingletonperformance (); }}
Supplemental Knowledge class loading mechanism

The static keyword is used to turn members of a class into class-related rather than instance-dependent, and static blocks are loaded when the class is first called, not when the object is created, so the static block has thread safety
-Normal initialization blocks
When Java creates an object, the system allocates memory for all instance variables of the object (provided that the class has already been loaded), and then initializes the instance variables, starting with the initial values specified when the initialization block or the instance variable is declared (both are executed in the same order as they were arranged in the source code) ), and then executes the initial value specified in the constructor.

    • Static initialization blocks
      aka Class initialization blocks (normal initialization blocks are responsible for object initialization, class initialization blocks are responsible for initializing classes). Static initialization blocks are class-dependent, and the system is statically initialized during the class initialization phase, rather than executing when the object is created. So static initialization blocks are always executed before the normal initialization block.

    • Execution order
      During class initialization and object initialization, the system not only executes the initialization block [static/non-static] of this class, but also traces back to the Java.lang.Object class, first executing the initialization block in the object class [Static/non-static] , then executes its parent class, and finally it is itself.
      Top-level Class (initialization block, constructor), parent class (initialization block, constructor), class (initialization block, constructor)

    • Summary
      static{} static initialization blocks are executed during class loading;
      {} is executed only during object initialization, but before the constructor;

Inner class
  • Internal class access permissions

    1. The Java external class has only two access rights: Public/default, while the inner class has four access rights: Private/default/protected/public. Internal classes can also use static adornments, and internal classes can have private access, protected access, public access, and package access. If the member inner class inner is decorated with private, it can only be accessed inside the external class, if it is modified with public, it will be accessible anywhere, if it is decorated with protected, it can only be accessed under the same package or inheriting the external class, if it is the default access permission. can only be accessed under the same package. This is a bit different from the external class, and the outer class can only be decorated with both public and package access permissions. A member inner class can be seen as a member of an external class, so you can have multiple permissions adornments like members of a class.
    2. The inner class is divided into members inner class and local inner class, relative to the inner class of the member is more extensive, the local inner class is used less (except anonymous inner class), and the member inner class is divided into static (static) inner class and non-static inner class, both of which have to abide by static and non-static constraints (such as static internal classes cannot access non-static members of external classes, etc.)
  • Non-static inner class

    1. When the non-static inner classes are used in the outside class, there is not much difference from the ordinary classes used in the normal class.
    2. Java does not allow static members to be defined in non-static inner classes, except for static final constant types
    3. If an external class member variable has the same name as a local variable inside a method in an inner class, then it can be distinguished by this, the external class names.
    4. A member of a non-static inner class can access the private member of an external class, but the other is not, and the members of the inner class are not perceived by the outer class. If an external class needs to access private members in an inner class, the creation of an internal class instance must be displayed, and the private permissions of the inner class will not work for the outer class:
  • Static Inner class

    1. Using static to decorate an inner class, the inner class belongs to the outer class itself, not to an object of the outer class.
    2. Static inner classes cannot access instance members of external classes because of the static function, and vice versa;
  • Anonymous inner class
    If the (method) local variable needs to be accessed by an anonymous inner class, the local variable needs to use the final decoration.

Enumeration
    1. The enumeration class inherits the Java.lang.Enum, not the object, so the enumeration cannot show inheritance of other classes; where enum implements the Serializable and comparable interfaces (implements comparable, Serializable);
    2. The non-abstract enum class uses the final adornment by default, so the enumeration class cannot derive subclasses;
    3. All instances of an enumeration class must be listed in the first row of the enumeration class (The enumeration class cannot create objects through new); And these instances are default/and can only be public static final;
    4. The constructor of the enumeration class is default/and can only be private;
    5. Enumeration classes should generally be designed as immutable classes, so it is recommended that member variables be decorated with private final;
    6. Enumeration classes cannot use the abstract keyword to declare an enumeration class as an abstract class (because the enumeration class does not allow subclasses), but if an enumeration class has an abstract method, or if an enumeration class implements an interface, defining each enumeration value must provide an implementation for the abstract method,

Java single-instance mode depth resolution

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.