[Negative tive Java distilled] item 3 enhances the singleton attribute by using a private constructor or enumeration type

Source: Internet
Author: User

About objective Java Distilled:

I have read this book tive Java for nearly two times on and off, and the content in it is very deep and helpful for improving the quality of engineering code. I plan to sort out a series slowly. The reason why I name it objective Java distilled is to organize the essence of this book as much as possible to facilitate review and use. After all, my memory is very limited, many things are often forgotten. In a word, it's just some reading notes for this book. The content in the article must be loyal to the original text. For some items, some content may be added, and I will mark the added content. At the same time, I hope this series of articles will help you. If you have any questions or suggestions, please leave a message. Thank you.

Outline:

  • Traditional Singleton Mode
  • Lazy mode to implement Singleton Mode
  • Use Enumeration type to implement Singleton Mode

 

It should be noted that, for the second point, the lazy mode achieves the singleton Mode
Java is not mentioned in the original article. This is for the sake of integrity, whether the self-added content has practical significance. It is still to be discussed, because the lazy implementation method, especially the use of IT in the concurrency environment, can bring about many benefits, it is really hard to say. In fact, I personally feel that it will not bring any benefits. The performance and space benefits brought about by this are not enough to offset the complexity it introduces.

 

In addition, the Code is obtained by directly referencing the original code snippet or rewriting it.

Traditional Singleton Mode

There are two main methods:

  1. Private constructor + public static domain
    + Eager implementation
  2. Private constructor + static factory Method
    + Eager implementation

 

For more information about the lazy method, see the next section.

 

We can find that the only difference between the two methods is that one is to expose the singleton object using the public static domain, and the other is to expose the object through the static factory method. There are two main advantages of using static factory methods:

  1. First, an indirect layer is introduced through the static factory method, and the Implementation can be changed in the method to meet the needs of changes, for example, whether a singleton is a global Singleton (that is, only this instance is available during the entire operation) or a thread Singleton (that is, each thread can have a unique instance of this object ).
  2. The second advantage is related to generics. The purpose is to enable the singleton object to exist across multiple types. For details, see related items in subsequent generics.

 

However, the benefits of these two advantages are often less important. In contrast, the use of public static domains is more concise. Relevant is used in the original article, and there is a strange saying that "it has a major effect". Here we will talk about the translation of the Chinese version of J. The translation is "related "...... Put it in the context, obviously it cannot be said.

The original article is as follows:

Often neither of these advantages is relevant,
And the final-field approach is simpler.

 

At the same time, both methods face the same problem, that is, the private constructor cannot guarantee that it will never be called by the outside. A client with a relevant priority can use accessibleobject. setaccessible to call the private constructor through reflection to obtain more instances. Therefore, in this case, you can add a judgment in the constructor as follows:

// Singleton with static factorypublic class Elvis {Private Static final Elvis instance = new ELVIS (); Private ELVIS () {If (instance! = NULL) {Throw new illegalstateexception ("the second instance cannot be created for a singleton class")} public static Elvis getinstance () {return instance ;}}

In addition, for serializable Singleton classes, you also need to customize a readresolve method to customize the objects returned during deserialization. Otherwise, each time during deserialization, A new instance is generated.

// readResolve method to preserve singleton propertyprivate Object readResolve() {// Return the one true Elvis and let the garbage collector// take care of the Elvis impersonator.return INSTANCE;}

LazySingleton Mode

The two implementation methods described above adopt the eager method, that is, the object will be created when the instance is defined. Of course, in addition to the eager policy, there are also lazy policies. That is, only staticinstance is defined, and Initialization is not performed immediately.

 

In a non-concurrent environment, the lazy implementation method is simple. When you get a singleton object, you can determine whether it is null. If it is null, initialize the singleton object first, then return.

// Singleton with static Factory (lazy Strategy) public class Elvis {Private Static final Elvis instance; private ELVIS () {If (instance! = NULL) {Throw new illegalstateexception ("the second instance cannot be created for a singleton class");} public static Elvis getinstance () {If (instance = NULL) {instance = new ELVIS ();} return instance ;}}

In a concurrent environment, the implementation of a single example in lazy mode is not that simple. The most important principle is that you cannot allow multiple threads to create more than one instance. Therefore, You need to lock the part of the instance:

// Singleton with static Factory (lazy strategy in concurrent environment) public class Elvis {Private Static Elvis instance; private ELVIS () {If (instance! = NULL) {Throw new illegalstateexception ("the second instance cannot be created for a singleton class");} public static Elvis getinstance () {If (instance = NULL) {synchronized {If (instance = NULL) {instance = new ELVIS () ;}} return instance ;}}

The above Code uses the double check lock technique, and it seems that there is no problem. To improve performance, we compress the synchronization code block to a relatively small scope, but this method has a lot of controversy, we recommend that you do not use it. For details, refer to the following link:

Http://www.ibm.com/developerworks/cn/java/j-dcl.html

This article in the link is relatively old. However, a detailed analysis of this issue is still instructive. The original Article is long. Here we will only talk about the key parts in this article:

The failure of double check locking is not attributed to JVM implementation, but to the memory model of the Java platform. The memory model allows "unordered write" is a major cause of failure.

The article also stressed that even if the volatile keyword is used, it cannot achieve the expected results. The reason is that most JVMs do not implement volatile correctly and therefore cannot rely on its behavior. This is because in Java
1.4, the volatile keyword function is not guaranteed, which has been corrected in 1.5.


According to the introduction of the volatile keyword in Java concurrency in practice, volatile exists as a lightweight synchronization mechanism, that is, it can suppress the code re-sorting by the compiler, at the same time, the volatile variable operations can be visible to other threads, ensuring the memory visibility of the variable. It is a lightweight synchronization mechanism because full synchronization, for example, a code block or method modified with synchronized often contains two semantics: (1) atomicity, (2) memory visibility, volatile can only ensure memory visibility.

In addition, Wikipedia also has a detailed discussion on this issue. For details, see:

Http://en.wikipedia.org/wiki/Double-checked_locking

 

A Brief Introduction of important points:

It mentioned that in j2se 5.0, the volatile keyword can work, and a code example is provided, which is directly referenced here:

// Works with acquire/release semantics for volatile// Broken under Java 1.4 and earlier semantics for volatileclass Foo {    private volatile Helper helper = null;    public Helper getHelper() {        Helper result = helper;        if (result == null) {            synchronized(this) {                result = helper;                if (result == null) {                    helper = result = new Helper();                }            }        }        return result;    }    // other functions and members...}

In addition, the Wiki also introduces a very good and clever way to implement lazy, and it is also thread-safe. By using internal static classes and JVM class loading mechanisms:

// Correct lazy initialization in Java @ThreadSafeclass Foo {    private static class HelperHolder {       public static Helper helper = new Helper();    }     public static Helper getHelper() {        return HelperHolder.helper;    }}

It utilizes the rule that internal static classes are loaded only when referenced.

In this way, once the internal helperholder is referenced, it will first be loaded by the JVM to initialize the static domain of the class, so that the Helper Singleton class will be initialized. The reason why it is thread-safe is also the blessing of JVM, because the process of JVM loading classes is thread-safe.

 

Use Enumeration type to implement Singleton Mode

Through the above analysis, the eager policy is simpler than the lazy policy. Lazy's strategy seems very intelligent, but it is really hard to say how much benefit this smart strategy can bring. In this case, you can refer to the famous saying "premature optimization is the source of all evil ". Indeed, it is better not to optimize this unknown effect. It introduces too much uncertainty. If we can determine that the bottleneck of the system lies in the implementation strategy of the singleton class, it is not too late to invest in optimization (but it will not become a bottleneck of the system in general ......).

The eager policy is implemented using the enumeration type of a single element, which is simpler and safer:

// Enum singleton - the preferred approachpublic enum Elvis {INSTANCE;public void leaveTheBuilding() { ... }}

This method helps you avoid problems caused by serialization and reflection.

As to why it can block serialization and reflection attacks, we will mention the enumeration type later.

 

Finally, we will reference the summary in the original article:

Although this method has not been popularized, it is indeed the best way to implement the singleton mode.

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.