[Reading Notes] headfirst design model-a single piece is not simple: details about all aspects to be considered to implement the singleton Model

Source: Internet
Author: User
(Refer:

Singleton design mode for a single instance

) What is a single piece?

A single piece ensures that a class has only one instance and provides a global access point to it. -- Gof

Is the single-piece mode simple?

Simple, simple, because there is only one class.

Single Piece is not simple!

In fact, a single piece is not easy to see, and it is a little complicated. Its complexity is caused by the fact that a single piece is designed to achieve the ambitious goal of "only one instance.

Of course, in general, a single piece is simple. HoweverLazy loading, concurrency, reflection, serialization, subclassAfter many factors, to ensure that only one instance is available, the complexity is greatly improved.

The following describes how to ensure that a single piece has only one instance and check whether it is as simple as imagined.

1. so easy! Ele. Me

It is easy to directly access static domains. To prevent modification, it is defined as final. Of course, constructors must be private.

/*** The simplest single-piece implementation, directly accessing the static domain ** @ author Nathan **/public class simplesingleton {public final static simplesingleton instance = new simplesingleton (); private simplesingleton () {} public void dosomething () {system. out. println ("simplesingleton. dosomething ");}}

2.1 variants

Access through static methods

/*** Simplest single-piece implementation, access static method ** @ author Nathan **/public class simplesingleton2 {private final static simplesingleton2 instance = new simplesingleton2 (); Private simplesingleton2 () {} public void dosomething () {system. out. println ("simplesingleton2.dosomething");} public static simplesingleton2 getinstance () {return instance ;}}

3.Complexity + 1:Call the private constructor to create an instance.

Implementation method. In the constructor, check whether it is null. Otherwise, an exception is thrown.

/*** Antireflection calls the single-piece Implementation of the constructor ** @ author Nathan **/public class antirefsingleton {public static antirefsingleton instance = new antirefsingleton (); Private antirefsingleton () {If (instance! = NULL) {Throw new runtimeexception ("this is a singleton class, please use antirefsingleton. instance to get the object! ") ;}} Public void dosomething () {system. Out. println (" simplesingleton. dosomething ");}}

3. Complexity + 2: lazy

To prevent useless parts from being loaded

/*** Single-piece Implementation of lazy loading, but there is a concurrency issue ** @ author Nathan **/public class lazysingleton {Private Static lazysingleton instance = NULL; private lazysingleton () {If (instance! = NULL) {Throw new runtimeexception ("this is a singleton class, please use the getinstance function to get the object! ") ;}} Public void dosomething () {system. out. println ("lazysingleton. dosomething ");} public static lazysingleton getinstance () {If (instance = NULL) {instance = new lazysingleton ();} return instance ;}}

4. Complexity + 3: Concurrency Control

The lazy is always unreliable and requires additional mechanisms to ensure thread security-DCL (double check and lock)

/*** Use DCL technology to implement concurrent and secure lazy loading of a single piece ** @ author Nathan **/public class concurrentsingleton {/*** must be declared as volatile */private static volatile concurrentsingleton instance = NULL; private concurrentsingleton () {If (instance! = NULL) {Throw new runtimeexception ("this is a singleton class, please use the getinstance function to get the object! ") ;}} Public void dosomething () {system. out. println ("concurrentsingleton. dosomething ");} public static concurrentsingleton getinstance () {If (instance = NULL) {// use the double check lock technology synchronized (concurrentsingleton. class) {If (instance = NULL) {instance = new concurrentsingleton () ;}} return instance ;}}

5. Lazy variants

It is thread-safe to use static internal classes.

/*** Use a static internal class to implement a lazy ticket, it is thread-safe ** @ author Nathan **/public class holdersingleton {Private Static class singletonholder {Private Static final holdersingleton instance = new holdersingleton ();} public static holdersingleton getinstance () {return singletonholder. instance ;}}

6. Complexity + 4: serialization

The author of objective Java has provided a solution, that is, to add the readresolve method. As follows:

/*** Serializable single-piece implementation (concurrent and secure lazy loading), but can only be used in the same JVM, cannot span JVM ** @ author Nathan **/public class serializablesingleton implements serializable {Private Static final long serialversionuid = 4151590550973506283l; private transient string description; Public void dosomething () {description = "serializablesingleton" ;}@ overridepublic string tostring () {return Super. tostring () + "[description =" + description + "]"; }/*** Must be declared as volatile */Private Static volatile transient serializablesingleton instance = NULL; private serializablesingleton () {If (instance! = NULL) {Throw new runtimeexception ("this is a singleton class, please use the getinstance function to get the object! ") ;}} Public static serializablesingleton getinstance () {If (instance = NULL) {// use the double check lock technology synchronized (serializablesingleton. class) {If (instance = NULL) {instance = new serializablesingleton () ;}} return instance;} private object readresolve () {// discard the deserialization instance, return the original Instance return instance ;}}
7. Complexity + 5: Cross-JVM serialization

In 6th, the solution author gave a solution to the problem, but did not actually solve the serialization problem. Because it can only adapt to the same JVM. However, serialization of Singleton in the same JVM does not seem to make much sense. The following is the kuajvm Singleton implementation method. Simply modify the readresolve method in Step 6.

/*** Serializable single-piece implementation that can be used across JVM (concurrent Security and lazy loading at the same time) ** @ author Nathan **/public class serializablesingleton2 implements serializable {Private Static final long serialversionuid = 4151590550973506283l; private string description; private int count; Public void dosomething () {description = "serializablesingleton2"; Count = 100 ;}@ overridepublic string tostring () {return Super. tostring () + "[description =" + descripti On + ", Count =" + Count + "]";} public void setcount (INT count) {This. count = count;} public int getcount () {return count;}/*** must be declared as volatile */Private Static volatile serializablesingleton2 instance = NULL; private serializablesingleton2 () {If (instance! = NULL) {Throw new runtimeexception ("this is a singleton class, please use the getinstance function to get the object! ") ;}} Public static serializablesingleton2 getinstance () {If (instance = NULL) {// use the double check lock technology synchronized (serializablesingleton2.class) {If (instance = NULL) {instance = new serializablesingleton2 () ;}} return instance;} private object readresolve () {If (instance = NULL) {// use the double check lock technology synchronized (serializablesingleton2.class) {If (instance = NULL) {instance = This; // if this is the first deserialization, use this instance; otherwise, no matter whether it is} return instance ;} public static void serialize (string file) throws exception {objectoutputstream OOS = new objectoutputstream (New fileoutputstream (new file); OOS. writeobject (instance);} public static void antiserialize (string file) throws exception {objectinputstream OIS = new objectinputstream (New fileinputstream (new file (File); instance = (serializablesingleton2) ois. readobject ();}}
8. Complexity + 6: Consider the inheritance of a Single Piece

In gof's design patterns, the solution is adopted by register. However, according to the description in the book, The subclass constructor must be public, which violates the original intention of the Singleton. Therefore, subclass instantiation must be realized through the "reflection" mechanism.

9. Complexity + 7: further consider cross-JVM serialization Based on the inherited single-piece System

(The code after the combination of 8 and 9 is as follows)

/*** Implementation of a subclass and serializable single piece ** @ author Nathan **/public class subablesingleton implements serializable {private string description; private int count; public void dosomething () {description = "subablesingleton"; Count = 100 ;}@ overridepublic string tostring () {return Super. tostring () + "[description =" + description + ", Count =" + Count + "]";} public void setcount (INT count) {This. count = count;} public int Getcount () {return count;} // the following code implements the Private Static final long serialversionuid = 5713856529741473199l; Private Static singletonholder holder = NULL; private string name; protected subablesingleton () {This (subablesingleton. class);} protected subablesingleton (class <? Extends subablesingleton> clazz) {If (holder. Lookup (clazz. getname ())! = NULL) {Throw new runtimeexception ("this is a singleton class, please use getinstance function to get the object! ");} Name = clazz. getname ();}/*** Note: Use synchronized instead of DCL for simple concurrency control ** @ Param clazz * @ return */public static synchronized subablesingleton getinstance (class <? Extends subablesingleton> clazz) {If (holder = NULL) {holder = new singletonholder ();} subablesingleton instance = holder. lookup (clazz. getname (); If (instance = NULL) {try {constructor <? Extends subablesingleton> constructor = clazz. getdeclaredconstructor (); constructor. setaccessible (true); instance = constructor. newinstance (); holder. register (clazz. getname (), instance);} catch (exception e) {e. printstacktrace () ;}} return instance;} private synchronized object readresolve () {system. out. println ("subablesingleton. readresolve "); If (holder = NULL) {holder = new singletonholder (); holder. register (this. name, this);} return holder. lookup (this. name);} public static void serialize (string file) throws exception {objectoutputstream OOS = new objectoutputstream (New fileoutputstream (new file); OOS. writeobject (holder);} public static void antiserialize (string file) throws exception {objectinputstream OIS = new objectinputstream (New fileinputstream (new file (File); holder = (singletonholder) ois. readobject ();}/*** class held by the Singleton, private ** @ author Nathan **/Private Static class singletonholder implements serializable {Private Static final long serialversionuid =-4221190210772287103l; private Map <string, subablesingleton> registry = new hashmap <string, subablesingleton> (); Public void register (string name, subablesingleton) {registry. put (name, subablesingleton);} public subablesingleton Lookup (string name) {return registry. get (name);} private synchronized object readresolve () {system. out. println ("singletonholder. readresolve "); // discard the deserialization instance and return the original Instance if (holder = NULL) {holder = This;} return holder ;}}}
/*** The subclass must call the parameter-containing constructor of the parent class in the constructor to complete anti-reflection control * @ author Nathan **/
public class SubSingleton extends SubableSingleton {private static final long serialVersionUID = 2430773476223417288L;protected SubSingleton() {super(SubSingleton.class);}}

So, do you still think that a single piece is simple? Welcome!

Refer:

Gof's Design Model

Tive Java

"Seven writing methods of single-piece mode" http://www.360doc.com/content/10/1213/09/2703996_77599342.shtml

Appendix: Unit Tests

Public class singletontest {@ testpublic void testsimplesingleton () {assert. assertequals (simplesingleton. instance, simplesingleton. instance) ;}@ testpublic void testsimplesingleton2 () {assert. assertequals (simplesingleton2.getinstance (), simplesingleton2.getinstance ();} @ testpublic void testantirefsingleton () throws exception {assert. assertequals (antirefsingleton. instance, antirefsingleton. instance); try {constructor <antirefsingleton> constructor = antirefsingleton. class. getdeclaredconstructor (); constructor. setaccessible (true); constructor. newinstance ();} catch (exception e) {e. printstacktrace () ;}@ testpublic void testlazysingleton () {assert. assertequals (lazysingleton. getinstance (), lazysingleton. getinstance () ;}@ testpublic void testconcurrentsingleton () {assert. assertequals (concurrentsingleton. getinstance (), concurrentsingleton. getinstance () ;}@ testpublic void testsubablesingleton () {assert. assertnotnull (subablesingleton. getinstance (subablesingleton. class); assert. asserttrue (subablesingleton. getinstance (subablesingleton. class) instanceof subablesingleton); assert. assertequals (subablesingleton. getinstance (subablesingleton. class), subablesingleton. getinstance (subablesingleton. class); assert. assertnotnull (subablesingleton. getinstance (subsingleton. class); assert. asserttrue (subablesingleton. getinstance (subsingleton. class) instanceof subsingleton); assert. assertequals (subablesingleton. getinstance (subsingleton. class), subablesingleton. getinstance (subsingleton. class);} @ testpublic void testsubablesingletonserialize () throws exception {subablesingleton instance = subablesingleton. getinstance (subablesingleton. class); subablesingleton instance2 = subablesingleton. getinstance (subsingleton. class); instance. dosomething (); instance2.dosomething (); subablesingleton. serialize ("testsubablesingletonserialize. jser "); subablesingleton. antiserialize ("testsubablesingletonserialize. jser "); assert. assertequals (instance, subablesingleton. getinstance (subablesingleton. class); assert. assertequals (instance2, subablesingleton. getinstance (subsingleton. class);} @ testpublic void testsubablesingletonserialize2 () throws exception {subablesingleton. antiserialize ("testsubablesingletonserialize. jser "); subablesingleton instance = subablesingleton. getinstance (subablesingleton. class); subablesingleton instance2 = subablesingleton. getinstance (subsingleton. class); assert. assertequals (100, instance. getcount (); assert. assertequals (100, instance2.getcount (); instance. setcount (20); subablesingleton. antiserialize ("testsubablesingletonserialize. jser "); instance = subablesingleton. getinstance (subablesingleton. class); assert. assertequals (20, instance. getcount () ;}@ testpublic void testsubablesingletonserialize3 () throws exception {subablesingleton instance = subablesingleton. getinstance (subablesingleton. class); instance. dosomething (); objectoutputstream OOS = new objectoutputstream (New fileoutputstream (new file ("testsubablesingletonserialize. jser "); OOS. writeobject (instance); objectinputstream OIS = new objectinputstream (New fileinputstream (new file ("testsubablesingletonserialize. jser "); subablesingleton instance2 = (subablesingleton) Ois. readobject (); assert. assertequals (instance, instance2) ;}@ testpublic void testsubablesingletonserialize4 () throws exception {objectinputstream OIS = new objectinputstream (New fileinputstream (new file ("testsubablesingletonserialize. jser "); subablesingleton instance2 = (subablesingleton) Ois. readobject (); assert. assertequals (100, instance2.getcount (); instance2.setcount (20); ois = new objectinputstream (New fileinputstream (new file ("testsubablesingletonserialize. jser "); instance2 = (subablesingleton) Ois. readobject (); assert. assertequals (20, instance2.getcount ();} @ testpublic void testsubablesingletonserialize5 () throws exception {subablesingleton instance = subablesingleton. getinstance (subsingleton. class); subablesingleton instance2 = subablesingleton. getinstance (subsingleton. class); instance. dosomething (); instance2.dosomething (); subablesingleton. serialize ("testsubablesingletonserialize5.jser"); subablesingleton. antiserialize ("testsubablesingletonserialize5.jser"); assert. assertequals (instance, subablesingleton. getinstance (subsingleton. class); assert. assertequals (instance2, subablesingleton. getinstance (subsingleton. class); assert. assertequals (100, instance2.getcount ();} @ testpublic void testserializablesingletoninonejvm () throws ioexception, classnotfoundexception {assert. assertequals (serializablesingleton. getinstance (), serializablesingleton. getinstance (); serializablesingleton Singleton = serializablesingleton. getinstance (); Singleton. dosomething (); system. out. println (Singleton); bytearrayoutputstream baos = new bytearrayoutputstream (); objectoutputstream OOS = new objectoutputstream (baos); OOS. writeobject (Singleton); bytearrayinputstream BAIS = new bytearrayinputstream (baos. tobytearray (); objectinputstream OIS = new objectinputstream (BAIS); serializablesingleton clone = (serializablesingleton) Ois. readobject (); system. out. println (clone); assert. assertequals (Singleton, clone); clone. dosomething ();}/*** in the same JVM, deserialization after serialization is invalid for a single piece, the JVM always uses the first single-piece instance ** @ throws exception */@ testpublic void testserializablesingleton2inonejvm () throws exception {assert. assertequals (serializablesingleton2.getinstance (), serializablesingleton2.getinstance (); serializablesingleton2 Singleton = serializablesingleton2.getinstance (); Singleton. dosomething (); assert. assertequals (serializablesingleton2.getinstance (). getcount (), 100); serializablesingleton2.serialize ("serializablesingleton2.jser"); // Changes the instance data serializablesingleton2.getinstance () after serialization (). setcount (30); // deserialization. The original Instance is used instead of the deserialization instance, because serializablesingleton2.antiserialize ("serializablesingleton2.jser") in the same JVM "); // Therefore, the value here is 30, instead of 100assert during serialization. assertequals (serializablesingleton2.getinstance (). getcount (), 30); // change the instance data serializablesingleton2.getinstance (). setcount (20); // deserialize serializablesingleton2.antiserialize ("serializablesingleton2.jser"); assert. assertequals (serializablesingleton2.getinstance (). getcount (), 20);}/*** start deserialization in another JVM <br> * Note: run the test testserializablesingleton2inonejvm first, run the test ** @ throws exception */@ testpublic void testserializablesingleton2notinonejvmread () throws exception {// deserialization and create a single-piece instance, later, this JVM will always use the instance serializablesingleton2.antiserialize ("serializablesingleton2.jser"); // Therefore, the value here is 100assert during serialization. assertequals (serializablesingleton2.getinstance (). getcount (), 100); // change the instance data serializablesingleton2.getinstance (). setcount (20); // deserialize serializablesingleton2.antiserialize ("serializablesingleton2.jser"); assert. assertequals (serializablesingleton2.getinstance (). getcount (), 20 );}}

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.