Single-Case mode
The simplest, but also very difficult.
To ensure that only one instance exists in a JVM, consider the following:
- Java can build objects using those methods
- Is Java still able to create only one instance of multithreaded concurrency when creating objects
Java methods for creating objects:
- New is most commonly used, and is created directly using the constructor. New instances are generated each time they are created. So the singleton should only be new once and return the value of the object when you want to use the object again
- Class.newinstance () This method calls the public's parameterless constructor.
To prevent this from being created, you can simply set the constructor to private. This is an error if the method is created again. Private constructors can also solve the problem of new everywhere.
- Reflection
Constructor CTT = C.getdeclaredconstructor ();
Ctt.setaccessible (TRUE);
T T1 = ctt.newinstance ();
Such a private constructor also does not. The workaround is to use an abstract class, which throws an exception and cannot be created. Or join in the constructor to judge if the second build throws an exception.
- Clone
This is primarily determined by the specific behavior of the Clone () method. This is not a problem if the Cloneable interface is not implemented.
- Deserialization
Deserialization will also break the single case, the way to solve is to write a readresolve. The rule of this method is that when deserializing, the return value of courage is replaced by the return value of the deserialization.
There is also an easier way to do this is not to implement the serializable interface, so the serialization of the time will be error.
Write a verification tool to verify that the class is a singleton
Public classSingletontester { Public Static<T>void checkclassnewinstance(class<t> c) {Try{T T1 = c.newinstance (); T t2 = c.newinstance ();if(T1! = t2) {System. out. println ("Class.newinstance checksum failed, can create two instances"); }Else{System. out. println ("Class.newinstance Verification Pass"); } }Catch(Exception e) {System. out. println ("cannot be created with class.newinstance, so the class.newinstance checksum is passed"); } } Public Static<T>void checkcontructorinstance(class<t> c) {Try{constructor<t> CTT = C.getdeclaredconstructor (); Ctt.setaccessible (true); T T1 = ctt.newinstance (); T t2 = ctt.newinstance ();if(T1! = t2) {System. out. println ("Contructorinstance checksum failed, can create two instances"); }Else{System. out. println ("Contructorinstance Verification Pass"); } }Catch(Exception e) {System. out. println ("cannot be created by reflection, so the contructorinstance checksum is passed"); } } Public Static<T>void testserializable(T t1) {File OBJECTF =NewFile ("/object"); ObjectOutputStream out=NULL;Try{ out=NewObjectOutputStream (NewFileOutputStream (OBJECTF)); out. writeobject (t1); out. Flush (); out. Close (); ObjectInputStreaminch=NewObjectInputStream (NewFileInputStream (OBJECTF)); T t2 = (t)inch. ReadObject ();inch. Close ();if(T1! = t2) {System. out. println ("Serializable checksum failed, can create two instances"); }Else{System. out. println ("Serializable verification Pass"); } }Catch(Exception e) {System. out. println ("cannot be created in deserialization, so the serializable checksum is passed"); } } Public Static void Main(string[] args) {checkclassnewinstance (Singleton3.class); Checkcontructorinstance (Singleton3.class); Testserializable (Singleton3.getinstance ()); }}
This tool verifies that class.newinstance attacks, reflection attacks, deserialization attacks, and the ability to block three of attacks are good ones.
Single Case 1
publicclass Singleton1{ privateSingleton1() { } privatestatic Singleton1 instance; publicstaticgetInstance(){ ifnull){ new Singleton1(); } return instance; }}
Single example of the most common lazy mode, private constructor, static method to obtain the instance, the time to obtain the first empty.
Test results:
不能用Class.newInstance创建,因此Class.newInstance校验通过ContructorInstance校验失败,可以创建两个实例不能用反序列化方式创建,因此Serializable校验通过
This class is not subject to deserialization attacks because it cannot be serialized
Because the private constructor avoids the class.newinstance
But it will be reflected and attacked.
In addition, it is not thread-safe
Single Case 2
publicclass Singleton2 { privatestaticnew Singleton2(); privateSingleton2(){}; publicstaticgetInstance(){ return sington; }}
To a typical a hungry man model.
Test results:
不能用Class.newInstance创建,因此Class.newInstance校验通过ContructorInstance校验失败,可以创建两个实例不能用反序列化方式创建,因此Serializable校验通过
There is also no problem of deserialization and class.newinstance.
And there's no concurrency problem.
However, it will initialize an instance at different times. Personal feelings actually have little impact
Single Case 3
The above will have a problem of reflection attack. To solve it.
Public classSingleton3 {Private StaticSingleton3 Sington =NewSingleton3 ();Private Static intCOUNT =0;Private Singleton3(){if(++count >1){Throw NewRuntimeException ("Can is construt more than once"); } }; Public StaticSingleton3getinstance(){returnSington; }}
Test results:
不能用Class.newInstance创建,因此Class.newInstance校验通过不能用反射方式创建,因此ContructorInstance校验通过不能用反序列化方式创建,因此Serializable校验通过
By adding a counter to resolve, this resolves the reflection attack, but is not thread-safe, in addition to introduce a new variable is not elegant. Here's a different way:
Single Case 4
publicabstractclass Singleton4 { privatestaticclass SingletonHolder{ privatestaticnew Singleton4() { }; } privateSingleton4(){}; publicstaticgetInstance(){ return SingletonHolder.INSTANCE; }}
This recommended use
- Use abstract classes to resolve reflection attacks
- Thread security loaded with a class solves the concurrency
- The purpose of Lazyloader is realized by using inner class.
- Clone not implemented
- There is no deserialization problem with serializable interface not implemented
Single Case 5
Here's the lazy mode without the inner class.
Public classSingleton5 {Private StaticSingleton5 Sington =NULL;Private Singleton5(){}; Public StaticSingleton5getinstance(){if(Sington = =NULL){//1Synchronized (Singleton5.class) {if(Sington = =NULL){//2Sington =NewSingleton5 (); } } }returnSington; }}
If there is no//1 check, then all getinstance () will enter the lock contention, which will affect performance, so the check is added.
In addition, it will be reflected and attacked.
Single Case 6
The
Thread security issue above is caused by the JVM's reordering mechanism:
Reorder:
The JVM will ensure that the results in single-threaded mode are correct when compiling, but the order of the code may be reordered or unordered, Mainly in order to better utilize the multi-CPU resources (disorderly order), as well as better use of registers,.
such as 1 a = 1; b = 2; a=3; three statements, if B is executed, may occupy the register position of a, the JVM may refer to the A=3 statement in front of the b=2, reducing the number of register replacements.
For example, instance = new Singleton5 () This part of the code has a pseudo-bytecode of:
1. Memory = allocate ()//Allocate RAM
2. Init (memory)//Initialize Object
3. I Nstance = memory//instance points to the address that was just initialized.
4. The first access to instance
is likely to be reordered at 2.3 in the JVM, because the JVM only guarantees that the result after the constructor has been executed is correct, but the order of execution may change. At this time, when calling getinstance concurrently, it is possible that the following occurs:
Time |
Thread A |
thread B |
T1 |
A1: Allocating memory space for an object |
|
T2 |
A3: Set instance point to memory space |
|
T3 |
|
B://1 to determine if instance is empty |
T4 |
|
B: Because instance is not NULL, thread B will return the object referenced by instance |
T5 |
|
B:instance not initialized, there may be an unknown problem |
T6 |
A2: Initializing objects |
|
T7 |
A: This is the object that is initialized. |
|
To solve this problem, we can consider it in two directions: stop reordering, or make reordering invisible to other threads.
A single example of how to stop reordering :
Use the volatile keyword provided after JDK1.5. The meaning of this keyword is to guarantee the visibility of the variable. Guaranteed variable changes will definitely write back the main memory and turn off some optimizations in java-server mode, such as reordering:
Public Abstract classSingleton6 {Private Static volatileSingleton6 Sington =NULL;Private Singleton6(){}; Public StaticSingleton6getinstance(){if(Sington = =NULL){//1Synchronized (Singleton6.class) {if(Sington = =NULL){//2Sington =NewSingleton6 () {};; } } }returnSington; }}
Yes, but the code is a little bit longer than Singleton4.
Single Case 7
to make reordering non-visible to other threads :
Public Abstract classSingleton7 {Private StaticSingleton7 Sington =NULL;Private Singleton7(){}; Public StaticSingleton7getinstance(){if(Sington = =NULL){//1Synchronized (Singleton7.class) {if(Sington = =NULL){//2Singleton7 temp =NewSingleton7 () {}; Sington = temp; } } }returnSington; }}
Another example of a 4 page is that reordering is not visible to other threads
Single Case 8
If serialization is necessary, then the serializable interface needs to be implemented, and the following is how this situation solves the problem of deserialization attacks
Public Abstract class Singleton8 implements Serializable{ Private Static class singletonholder{ Private Static FinalSingleton8 INSTANCE =NewSingleton8 () {}; }Private Singleton8(){}; Public StaticSingleton8getinstance(){returnSingletonholder.instance; } PublicObjectReadresolve() {returnSingletonholder.instance; }}
Test results:
不能用Class.newInstance创建,因此Class.newInstance校验通过不能用反射方式创建,因此ContructorInstance校验通过Serializable校验通过
This is primarily the method Readresolve, whose return result is used instead of the deserialized result
Single Case 9
Enumeration Singleton,recommended in Effectivejava
The last one. is to use an enumeration singleton. You can look at it, it's very useful.
publicenum SingleEnum { INSTANCE; }
Test results:
不能用Class.newInstance创建,因此Class.newInstance校验通过不能用反射方式创建,因此ContructorInstance校验通过Serializable校验通过
It also succeeded in avoiding all possible problems:
- Use abstract classes to resolve reflection attacks
- Thread security loaded with a class solves the concurrency
The code for its class loading section:
publicabstractclass Enum{ private Enum{} privatestaticnull; static{ new Enum(){}; }}
- Initializing with a static method guarantees thread safety, which is initialized when the class is loaded.
- Clone not implemented
- There will be no deserialization problem, and this use of JAVAP still does not see the source code similar to ReadObject, and it should be something to do with the bytecode generated inside the JDK.
All right, all in all, try to use an enumeration, or a holder singleton.
Probably the most complete example of Java Singleton mode discussion