Java Notes 00 - Singleton Pattern(单例总结)

简介: 转:http://hukai.me/java-notes-singleton-pattern/   这里不赘述单例模式的概念了,直接演示几种不同的实现方式。 0)Eager initialization 如果程序一开始就需要某个单例,并且创建这个单例并不那么费时,我们可以考虑用这种方式: ...

转:http://hukai.me/java-notes-singleton-pattern/

 

这里不赘述单例模式的概念了,直接演示几种不同的实现方式。

0)Eager initialization

如果程序一开始就需要某个单例,并且创建这个单例并不那么费时,我们可以考虑用这种方式:

1
2
3
4
5
6 7 8 9 
public class Singleton {  private static final Singleton INSTANCE = new Singleton();   private Singleton() {}   public static Singleton getInstance() {  return INSTANCE;  } } 

这种实现方式有几个特点:

  • 实例一开始就被创建(Eager initialization)。
  • getInstance()方法不需要加synchronize来解决多线程同步的问题。
  • final关键字确保了实例不可变,并且只会存在一个。

1)Lazy initialization

懒加载的方式使得单例会在第一次使用到时才会被创建.先看一种有隐患的写法:

1
2
3
4
5
6 7 8 9 10 11 12 13 14 15 16 
public final class LazySingleton {  private static volatile LazySingleton instance = null;   // private constructor  private LazySingleton() {  }   public static LazySingleton getInstance() {  if (instance == null) {  synchronized (LazySingleton.class) {  instance = new LazySingleton();  }  }  return instance;  } } 

请注意:上面的写法其实非线程安全的,假设两个Thread同时进入了getInstance方法,都判断到instance==null,然后会因为synchronized的原因,逐个执行,这样就得到了2个实例。解决这个问题,需要用到典型的double-check方式,如下:

1
2
3
4
5
6 7 8 9 10 11 12 13 14 15 16 17 
public class LazySingleton {  private static volatile LazySingleton instance = null;   private LazySingleton() {  }   public static LazySingleton getInstance() {  if (instance == null) {  synchronized (LazySingleton .class) {  if (instance == null) {  instance = new LazySingleton ();  }  }  }  return instance;  } } 

另外一个更简略直观的替代写法是:

1
2
3
4
5
6 7 8 9 10 11 12 13 
public class LazySingleton {  private static volatile LazySingleton instance = null;   private LazySingleton() {  }   public static synchronized LazySingleton getInstance() {  if (instance == null) {  instance = new LazySingleton ();  }  return instance;  } } 

2)Static block initialization

如果我们对程序的加载顺序有点了解的话,会知道Static block的初始化是执行在加载类之后,Constructor被执行之前。

1
2
3
4
5
6 7 8 9 10 11 12 13 14 15 16 17 18 19 
public class StaticBlockSingleton {  private static final StaticBlockSingleton INSTANCE;   static {  try {  INSTANCE = new StaticBlockSingleton();  } catch (Exception e) {  throw new RuntimeException("Error, You Know This, Haha!", e);  }  }   public static StaticBlockSingleton getInstance() {  return INSTANCE;  }   private StaticBlockSingleton() {  // ...  } } 

上面的写法有一个弊端,如果我们类有若干个static的变量,程序的初始化却只需要其中的1,2个的话,我们会做多余的static initialization。

3)Bill Pugh solution

University of Maryland Computer Science researcher Bill Pugh有写过一篇文章initialization on demand holder idiom

1
2
3
4
5
6 7 8 9 10 11 12 13 14 15 16 
public class Singleton {  // Private constructor prevents instantiation from other classes  private Singleton() { }   /**  * SingletonHolder is loaded on the first execution of Singleton.getInstance()  * or the first access to SingletonHolder.INSTANCE, not before.  */  private static class SingletonHolder {  public static final Singleton INSTANCE = new Singleton();  }   public static Singleton getInstance() {  return SingletonHolder.INSTANCE;  } } 

SingletonHolder类会在你需要的时候才会被初始化,而且它不影响Singleton类的其他static成员变量的使用。这个方法是线程安全的并且避免了使用volatile与synchronized。

4)Using Enum

这是最简便安全的方法。没有明显的缺点,并且避免了下面要讲到的序列化的隐患。

1
2
3
4
5
6 
public enum Singleton {  INSTANCE;  public void execute (String arg) {  // perform operation here  } } 

Serialize and de-serialize

在某些情况下,需要实现序列化的时候,普通的单例模式需要添加readResolve的方法,不然会出现异常。

1
2
3
4
5
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 
public class DemoSingleton implements Serializable {  private volatile static DemoSingleton instance = null;   public static DemoSingleton getInstance() {  if (instance == null) {  instance = new DemoSingleton();  }  return instance;  }   protected Object readResolve() {  return instance;  }   private int i = 10;   public int getI() {  return i;  }   public void setI(int i) {  this.i = i;  } } 

仅仅有上面的还不够,我们需要添加serialVersionUID,例子详见下面的总结。

Conclusion

实现一个功能完善,性能更佳,不存在序列化等问题的单例,建议使用下面两个方式之一:

Bill Pugh(Inner Holder)

1
2
3
4
5
6 7 8 9 10 11 12 13 14 15 16 17 18 19 
public class DemoSingleton implements Serializable {  private static final long serialVersionUID = 1L;   private DemoSingleton() {  // private constructor  }   private static class DemoSingletonHolder {  public static final DemoSingleton INSTANCE = new DemoSingleton();  }   public static DemoSingleton getInstance() {  return DemoSingletonHolder.INSTANCE;  }   protected Object readResolve() {  return getInstance();  } } 

Enum

1
2
3
4
5
6 
public enum Singleton {  INSTANCE;  public void execute (String arg) {  // perform operation here  } } 

参考资料

目录
相关文章
|
7月前
|
设计模式 Java
26、Java 简单实现单例设计模式(饿汉式和懒汉式)
26、Java 简单实现单例设计模式(饿汉式和懒汉式)
58 2
|
7月前
|
设计模式 安全 Java
在Java中即指单例设计模式
在Java中即指单例设计模式
41 0
|
2月前
|
缓存 安全 Java
Singleton:在 Java 编程中编写和使用的 6 种方法
Singleton:在 Java 编程中编写和使用的 6 种方法
29 0
|
7月前
|
设计模式 安全 Java
【JAVA】Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式
【JAVA】Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式
|
4月前
|
存储 JSON 前端开发
【Java】用@JsonFormat(pattern = “yyyy-MM-dd“)注解,出生日期竟然年轻了一天
在实际项目中,使用 `@JsonFormat(pattern = "yyyy-MM-dd")` 注解导致出生日期少了一天的问题,根源在于夏令时的影响。本文详细解析了夏令时的概念、`@JsonFormat` 注解的使用方法,并提供了三种解决方案:在注解中添加 `timezone = GMT+8`、修改 JVM 参数 `-Duser.timezone=GMT+08`,以及使用 `timezone = Asia/Shanghai
433 0
【Java】用@JsonFormat(pattern = “yyyy-MM-dd“)注解,出生日期竟然年轻了一天
|
4月前
|
设计模式 安全 Java
|
4月前
|
设计模式 人工智能 Java
Java 如何使用单例类
Java 如何使用单例类
18 1
|
4月前
|
设计模式 Java
【Java】单例设计模式
【Java】单例设计模式
|
5月前
|
设计模式 存储 安全
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
66 1
|
4月前
|
设计模式 SQL 安全
单例模式大全:细说七种线程安全的Java单例实现,及数种打破单例的手段!
设计模式,这是编程中的灵魂,用好不同的设计模式,能使你的代码更优雅/健壮、维护性更强、灵活性更高,而众多设计模式中最出名、最广为人知的就是Singleton Pattern单例模式。通过单例模式,我们就可以避免由于多个实例的创建和销毁带来的额外开销,本文就来一起聊聊单例模式。