Easy to master the Java singleton pattern

  • 2020-05-10 18:07:00
  • OfStack

The singleton pattern is the simplest of the 23 design patterns, and it is particularly useful in enterprise development. The advantage of the singleton pattern is that there is only one instance in the project.

Features: constructor privatization, object privatization, only provide 1 external access interface.

Application scenarios:

      1, the system needs to share resources: such as the logging system, spring's resource manager, and so on

      2. To control the use of resources: such as thread pools

Common applications in enterprise development and common frameworks:

      J2EE, beans Spring resource manager (beans), database connection pool, thread pool, logging system, website counter, etc

Singleton pattern classification:

1. Hunhan mode: hunhan mode is the simplest singleton mode of the code, but the instance is loaded when the class is initialized. In the case of not using it immediately, it will slow down the loading speed of the system.


public class Singleton{
 private static Singleton instance = new Singleton();
 
 private Singleton(){}
 
 public static Singleton getInstance(){
 return instance;
 }
}

2. Lazy mode: compared with the hungry mode, lazy mode is handled in the external interface of only 1 through instantiation, which realizes lazy loading and saves the initialization time of the system. However, there exists the situation of unsafe thread.


public class Singleton{
 private static Singleton instance = null;
 
 private Singleton(){}
 
 public static Singleton getInstance(){
 if(instance == null){
  return new Singleton();
 }
 return instance;
 }
}

3, double check lock: double check lock mode is the lazy mode upgrade, let the lazy mode become thread safety. Note: there is a memory problem with the double check lock, which may invalidate the double check lock.


public class Singleton{
 private static Singleton instance = null;
 
 private Singleton(){}
 
 public static Singleton getInstance(){
 if(instance == null){
  synchronized(Singleton.class){
  if(instance == null){
   return new Singleton();
  }
  }
 }
 return instance;
 }
}

4, static internal class mode: static internal class has both lazy mode and bad mode: thread safety, lazy loading.


public class Singleton{
 private static class SingletonFactory{
 private static Singleton INSTANCE = new Singleton();
 }
 
 private Singleton(){}
 
 public static Singleton getInstance(){
 return SingletonFactory.INSTANCE;
 }
}

5, enumeration class pattern: should be the most perfect simple interest pattern, not only thread safety, but also to prevent anti-sequence and reflection problems.


enum Singleton{
 INSTANCE;
 
 public void doSomething(){
 ...
 }
}


Details of the singleton pattern:

1. Reflection breaks the singleton pattern: reflection can break the implementation of the singleton pattern (except the enumerated class pattern)      


/**
 * The singleton pattern is broken by reflection  
 */
public class Demo01 {

 public static void main(String[] args) throws Exception {
 Singleton s1 = Singleton.getInstance();
 Singleton s2 = Singleton.getInstance();
 
 System.out.println(s1 == s2);
 
 Class<Singleton> clazz = (Class<Singleton>) Class.forName("com.singleton.Singleton");
 Constructor<Singleton> constructor = clazz.getDeclaredConstructor(null);
 constructor.setAccessible(true);
 Singleton s3 = constructor.newInstance();
 
 System.out.println(s1 == s3);
 }
}

class Singleton{
 private static Singleton instance = new Singleton();
 
 private Singleton(){
 // A way to prevent reflection from breaking the simple interest mode , Open the comments section 
// if(instance != null){
//  throw new RuntimeException();
// }
 }
 
 public static Singleton getInstance(){
 return instance;
 }
}

In fact, prevention means that it cannot be created by reflection.

2. Deserialization breaks singleton mode (except enumeration class mode)


/**
 *  Deserialization breaks the singleton pattern 
 */
public class Demo02 {

 public static void main(String[] args) throws Exception {
 Singleton s1 = Singleton.getInstance();
 Singleton s2 = Singleton.getInstance();
 
 System.out.println(s1 == s2);
 
 FileOutputStream fos = new FileOutputStream("d://test.txt");
 ObjectOutputStream oos = new ObjectOutputStream(fos);
 oos.writeObject(s1);
 
 oos.close();
 fos.close();
 
 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d://test.txt"));
 Singleton s3 = (Singleton) ois.readObject();
 
 System.out.println(s1 == s3);
 }
}
class Singleton implements Serializable{
 private static Singleton instance = new Singleton();
 
 public static Singleton getInstance(){
 return instance;
 }
 
 // When deserializing, this method is called if the object already exists 
// private Object readResolve() throws ObjectStreamException{
// return instance;
// 
// }
}

These two situations are limited to understanding and are not used much in the actual development process.

At this point, the singleton pattern is complete.


Related articles: