Singleton Pattern in Java Design Patterns

Reprinted: https://www.cnblogs.com/zhengyu940115/p/6652465.html


Among the design patterns, the most commonly talked about is the singleton design pattern.

Baidu Encyclopedia explains the design motivation of the singleton design pattern as follows:

The above is a general concept, so what does a singleton bring in specific development?

In the Java language, singletons bring two major benefits:

1. For frequently used objects, the time spent on creating objects can be omitted, which is a considerable system overhead for those heavyweight objects.

2. Since the number of new operations is reduced, the frequency of system memory usage will also be reduced, which will reduce the GC pressure and shorten the GC pause time.

Therefore, for the key components of the system and objects that are frequently operated, using the singleton pattern can effectively improve the system performance.

The participants of the singleton are very simple, only the singleton class and the user;

The following describes the specific implementation of the singleton design pattern in java code:

The core of the singleton pattern is to return a unique object instance through an interface. A simple singleton implementation is as follows:

copy code
1  public  class Singleton {
 2      private   Singleton() {
 3          System.out.println("Singleton is create"); // The process of creating a singleton may be slower 
4      }
 5  
6      private  static Singleton instance = new Singleton();
 7  
8      private  static Singleton getInstance() {
 9          return instance;
 10      }
 11 }
copy code

Pay attention to the blue part of the code. First of all, the singleton class must have a private access level constructor. Only in this way can it ensure that the singleton will not be instantiated in other code in the system, which is very important; secondly , the instance member and the getInstance() method must be static.

这种单例的实现方式非常简单,而且十分可靠,它唯一的不足仅是无法对instance实例做延迟加载,假如单例的创建过程很慢,而由于instance成员变量是static定义的,因此在JVM加载单例类时,单例对象就会被建立,如果此时,这个单例类在系统中还扮演其他角色,那么在任何使用这个单例类的地方都会初始化这个单例变量,根本就不会管是否被用到。比如单例String工厂,用于创建一些字符串(该类既用于创建单例Singleton,又用于创建String对象)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public  class  Singleton {
     private  Singleton() {
         System.out.println( "Singleton  is  create" );  // 创建单例的过程可能会比较慢
     }
 
     private  static  Singleton instance =  new  Singleton();
 
     private  static  Singleton getInstance() {
         return  instance;
     }
     
     public  static  void  CreateString(){        //这是模拟单例类扮演其他角色
         System.out.println( "createString in Singleton" );
     }
}

  上面代码的意思就是,假如单例类里面有getInstance以外的其它静态方法,跟单例没啥关系的方法,如果使用了Singleton.CreateString()这种调用,就会自动创建Singleton这个类实例(虽然private构造已经防止了你人为去new这个单例),这是开发人员不愿看到的,我运行下面代码(此代码调用了上面代码的createString()方法)进行展示:

 

 那么在这个单例基础上进行延迟加载改良:

copy code
 1 public class Singleton {
 2     private Singleton() {
 3         System.out.println("LazySingleton  is  create"); // 创建单例的过程可能会比较慢
 4     }
 5 
 6     private static Singleton instance = null;
 7 
 8     public static synchronized Singleton getInstance() {
 9         if (instance == null)
10             instance = new Singleton();
11         return instance;
12     }
13 
14 }
copy code

此时再调用同样的测试方法:

解决了这个问题。

 

显而易见,这次改良主要是从JVM加载类的原理上进行改良的,上面的代码在初始化类的时候给instance赋予null,确保系统启动的时候没有额外的负载,其次,在getInstance方法中加入判断单例是否存在,不存在才new。但是getInstance()方法必须是同步的,

否则多线程环境下,可能线程1正在创建单例时,线程2判断单例instance为空,就会创建多个实例。

 

但是上面的写法,存在性能问题,在多线程下,它的耗时远远大于第一种单例。以下代码就说明了此问题:

copy code
1     @Override
2         public void run(){
3             for (int i = 0; i < 100000; i++) 
4                 Singleton.getInstance();
5                 System.out.println("spend:" (System.currentTimeMillis()-begintime));
6             
7         }
copy code

开启五个线程同时完成以上代码的运行,使用第一种单例耗时0ms,而使用第二种单例耗时390ms,性能至少相差两个数量级。

为了线程安全使用了同步关键字反而降低了系统性能,为了解决这个问题,继续改进:

copy code
 1 public class Singleton {
 2     private Singleton() {
 3         System.out.println("LazySingleton  is  create"); // 创建单例的过程可能会比较慢
 4     }
 5 
 6     public static class SingletonHolder {
 7         private static Singleton instance = new Singleton();
 8     }
 9 
10     public static synchronized Singleton getInstance() {
11         return SingletonHolder.instance;
12     }
13 
14 }
copy code

That's right, this is the most complete singleton writing method. The inner class implements a singleton. Except for the enumeration singleton implementation described in effctive java, this method is currently the best way to implement it.

In this implementation, the inner class is used to protect the singleton. When the Singleton class is loaded, the inner class will not be initialized, so it can be ensured that the singleton class will not be initialized when the Singleton class is loaded into the JVM. When the getInstance method is called At the same time, since the establishment of the instance is completed when the class is loaded, it is inherently friendly to multi-threading, and the getInstance() method does not need to be decorated with synchronized. Therefore, this implementation can take into account The advantages of the first two ways of writing (lazy loading, asynchronous).

 Finally, in extreme cases, serialization and deserialization may destroy the singleton. Generally speaking, it is rare. If it exists, pay more attention. At this time, you can add the following code:

1
2
3
private  Object readResolve() {
         return  instance;
     }

For singleton learning, you can also refer to the following articles:

https://blog.csdn.net/JQ_AK47/article/details/54894793

https://blog.csdn.net/qq_32736689/article/details/51082418



Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325810459&siteId=291194637