on
Java高并发 - 14.单例设计模式
本文介绍7种实现单例设计模式的方法。
饿汉式
// final不允许被继承
public final class Singleton {
// 实例变量
private byte[] data = new byte[1024];
// 在定义实例对象的时候直接初始化
private static Singleton instance = new Singleton();
// 私有构造方法,不允许外部new
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
instance作为类变量在类初始化的过程中会被收集进
懒汉式
使用的时候再去创建,避免类在初始化时提前创建。
public final class Singleton {
private byte[] data = new byte[1024];
// 定义实例,但是不直接初始化
private static Singleton instance = null;
private Singleton() {}
privat static Singleton getInstance() {
if (null == instance)
instance = new Singleton();
return instance;
}
}
getInstance方法放在多线程环境下并不是安全的,并不能保证单例的唯一性。当两个线程同时看到null==instance,instance将被new两次。
懒汉式 + 同步方法
public final class Singleton {
private byte[] data = new byte[1024];
private static Singleton instance = null;
private Singleton() {}
private static synchronized Singleton getInstance() {
if (null == instance)
instance = new Singleton();
return instance;
}
}
用synchronize修饰getInstance方法,能保证instance实例的唯一性。但是由于其排他性导致了getInstance方法只能在同一时刻被一个线程所访问,性能低下。
Double-Check
public final class Singleton {
private byte[] data = new byte[1024];
private static Singleton instance = null;
Connection conn;
Socket socket;
private Singleton() {
this.conn // 初始化conn
this.socket // 初始化socket
}
public static Singleton getInstance() {
// 当instance为null时,进入同步代码块,并避免了每次都需要进入同步代码块
if (null == instance) {
// 只有一个线程能够获得Singleton.class关联的monitor
synchronized (Singleton.class) {
// 判断如果instance为null时创建
if (null == instance) {
instance = new Singleton();
}
}
}
return instance;
}
}
这种方式既满足了懒加载,有保证了instance实例的唯一性,Double-Check的方式又提供了高效的数据同步策略,可以允许多个线程同时对getInstance进行访问。但是这种方式在多线程的情况下有可能会引起空指针异常。
在Singleton的构造方法中,需要分别实例化conn和socket两个资源,还有Singleton自身。由于可能发生指令重排,可能instance最先被实例化,而conn和socket并未完成实例化。此时另一个线程判断到instance不为null,即可返回,但conn和socket还未创建完成。
Volatile + Double-Check
Double-Check方式会出现指令重排导致多线程不安全问题,可以通过volatile关键字防止重排发生。
private volatile static singleton instance = null;
Holder方式
public final class Singleton {
private byte[] data = new byte[1024];
private Singleton() {}
// 在静态内部类中持有Singleton的实例,并且可被直接初始化
private static class Holder {
private static Singleton instance = new Singleton();
}
// 调用getInstance方法,事实上是获得Holder的instance静态属性
public static Singleton getInstance() {
return Holder.instance;
}
}
在Singleton类中,将静态成员instance放到静态内部类Holder之中。因此,在Singleton类的初始化过程中不会创建Singleton实例,Holder类中定义了Singleton的静态变量,并直接进行了实例化。当Holder被主动引用时会创建Singleton的实例。创建Singleton的过程被收集到
枚举方式
public enum Singleton {
INSTANCE;
private byte[] data = new byte[1024];
Singleton() {
System.out.println("INSTANCE will be initialized immediately");
}
public static void method() {
// 调用该方法会主动使用Singleton,INSTANCE将会被实例化
}
public static Singleton getInstance() {
return INSTANCE;
}
}
枚举类型不允许被继承,同样是线程安全的且只能被实例化一次。但枚举类型不能够懒加载,对Singleton主动使用,比如调用其中的静态方法,则INSTANCE会立即实例化。
可以对其进行改造,采用类似Holder的方式,增加懒加载的特性。
public class Singleton {
private byte[] data = new byte[1024];
private Singleton() {}
// 使用枚举充当Holder
private enum EnumHolder {
INSTANCE;
private Singleton instance;
EnumHolder() {
this.instance = new Instance();
}
private Singleton getSingleton() {
return instance;
}
}
public static Singleton getInstance() {
return EnumHolder.INSTANCE.getSingleton();
}
}