单例模式大全

注意点

  1. lazy loading
  2. 线程安全
  3. jvm 创建对象顺序

一、饿汉

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

public class Singleton {  
     private  static Singleton instance = null;  
     static {  
         instance = new Singleton();  
      }
     private Singleton (){}
     public static Singleton getInstance() {  
         return instance;  
     }  
 }  
  1. 基于classloder机制避免了多线程的同步问题
  2. 导致类装载的时机很多,所以有可能达不到lazy loading

二、静态内部类

 public class Singleton {  
      private static class SingletonHolder {  
          public static final Singleton INSTANCE = new Singleton();  
      }  
      private Singleton (){}
      public static final Singleton getInstance() {  
          return SingletonHolder.INSTANCE;  
      }  
  } 
  1. 基于classloder机制避免了多线程的同步问题
  2. 只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance,实现lazy loading

三、懒汉

 public class Singleton {  
      private volatile static Singleton instance;  
      private Singleton (){}
      public static synchronized Singleton getInstance() {  
      if (instance == null) {  
          instance = new Singleton();  
      }  
      return instance;  
      }  
 } 
  1. 效率很低,因为99%情况下不需要同步

四、双重检查锁定

public class SafeDoubleCheckedLocking {
    private volatile static Instance instance;
    public static Instance getInstance() {
        if (instance == null) {
            synchronized (SafeDoubleCheckedLocking.class) {
                if (instance == null)
                    instance = new Instance();//instance为volatile,现在没问题了
            }
        }
        return instance;
    }
}
  1. 双重检查锁定示例代码的第7行(instance = new Singleton();)创建一个对象。这一行代码可以分解为如下的三行伪代码:
memory = allocate();   //1:分配对象的内存空间
ctorInstance(memory);  //2:初始化对象
instance = memory;     //3:设置instance指向刚分配的内存地址
  1. 上面三行伪代码中的2和3之间,可能会被重排序(在一些JIT编译器上,这种重排序是真实发生的)。2和3之间重排序之后的执行时序如下:
memory = allocate();   //1:分配对象的内存空间
instance = memory;     //3:设置instance指向刚分配的内存地址
                       //注意,此时对象还没有被初始化!
ctorInstance(memory);  //2:初始化对象
  1. volatile 避免了 2、3的重排
  2. 通过对比基于volatile的双重检查锁定的方案和静态内部类的方案,我们会发现静态内部类的方案的实现代码更简洁。但基于volatile的双重检查锁定的方案有一个额外的优势:除了可以对静态字段实现延迟初始化外,还可以对实例字段实现延迟初始化。
  3. 讨论下volatile关键字的必要性,如果没有volatile关键字,问题可能会出在singleton = new Singleton();这句,用伪代码表示
inst = allocat(); // 分配内存  
sSingleton = inst;      // 赋值
constructor(inst); // 真正执行构造函数

可能会由于虚拟机的优化等导致赋值操作先执行,而构造函数还没完成,导致其他线程访问得到singleton变量不为null,但初始化还未完成,导致程序崩溃。

五、枚举

public enum Singleton {  
     INSTANCE;  
     public void whateverMethod() {  
     }  
 }  

上边四种单例方式都有一个弊端,都可以通过反射调用私有构造方法进行实例化。但通过枚举做单例就不会有这个问题。具体原理参见:http://www.jianshu.com/p/ec811fc70b08 这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊,不过,个人认为由于1.5中才加入enum特性,用这种方式写不免让人感觉生疏,在实际工作中,我也很少看见有人这么写过。

http://www.infoq.com/cn/articles/double-checked-locking-with-delay-initialization

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值