码迷,mamicode.com
首页 > 编程语言 > 详细

[Java] [Singleton] [DCL]

时间:2017-11-01 10:20:23      阅读:217      评论:0      收藏:0      [点我收藏+]

标签:懒汉   指令重排序   意思   single   操作   自己的   原子操作   col   可见性   

Singleton

  • 只能有一个实例;必须自己创建自己的实例;必须给其他所有对象提供这一实例

实现方法

饿汉式singleton

  • 预先加载法
  • class Single {
      private Single() {
        System.out.println("ok");
      }
      
      private static Single instance = new Single();
      
      public static Single getInstance() {
        return instance;
      }
    }
  • 优点:
    • thread safe
    • 调用时速度快(在类加载时已经创建好一个static对象)
  • 缺点:
    • 资源利用率不高(可能系统不需要)
    • 在一些场景下无法使用。比如在single实例的创建依赖参数或配置文件时。

懒汉式singleton

  • 延迟加载法
  • public class LazySingleton {
      private static LazySingleton instance;
      private LazySingleton() {}
      
      public static LazySingleton getInstance() {
        if (instance == null) {
          instance = new LazySingleton();
        }
        return instance;
      }
    }
  • 适用于单线程环境,not trhead-safe,getInstance()方法可能返回两个不同实例。
  • 可以改成thread-safe版本,如下:
    public class LazySingleton {
        private static LazySingleton instance;    
        private LazySingleton() {}
        
        public static synchronized LazySingleton getInstance() {             
    		if (instance == null) {                       
    			instance = new LazySingleton();           
    		}
            return instance;                                      
        }
    }
  • 优点:不执行getInstance对不会被实例化
  • 缺点:第一次加载时反应不快。每次调用getInstance的同步开销大。(大量不必要的同步)

DCL singleton

  • Double Check Lock
  • 避免每次调用getInstance方法时都同步
  • public class LazySingleton {
      private static LazySingleton instance;
      private LazySingleton() {}
      
      public static LazySingleton getInstance() {
        if (instance == null) {
          synchronized(LazySingleton.class) {
            if (instance == null) {
              instance = new LazySingleton();
            }
          }
        }
        return instance;
      }
    }
  • 第一层判断,避免不必要的同步。第二层判断则是在线程安全的情况下创建实例。
  • 优点:资源利用率高,多线程下效率高。
  • 缺点:第一次加载时反应不快,由于java内存模型一些原因偶尔会失败,在高并发下有一定的缺陷。
  • 上述代码依然存在不安全性
    instance = new LazySingleton()这条语句实际上不是一个原子操作,它大概包括三件事:
    1. 给LazySingleton的实例分配内存;
    2. 初始化LazySingleton()的构造器;
    3. 将instance对象指向分配的内存空间(在这一步的时候instance变成非null)。

  但是由于Java编译器允许处理器乱序执行(指令重排序),上述2、3点的顺序是无法保证的。(意思是可能instance != null时有可能还未真正初始化构造器)。 
  解决方法是通过将instance定义为volatile的。(volatile有两个语义:1. 保证变量的可见性;2. 禁止对该变量的指令重排序)

  • 参考<<Java并发编程>> P286 ~ P287。在JMM后续版本(>= Java5.0)中,可以通过结合volatile的方式来启动DCL,并且该方式对性能的影响很小。然而,DCL的这种使用方式已经被广泛地抛弃了。
  • (因为volatile屏蔽指令重排序的语义在JDK1.5中才被完全修复,此前的JDK中即使将变量声明为volatile,也仍然不能完全避免重排序所导致的问题,这主要是因为volatile变量前后的代码仍然存在重排序问题。)

static内部类singleton

  • class Single {
      private Single() {}
      
      private static class InstanceHolder {
        private static final Single instance = new Single();
      }
      
      public static Single getInstance() {
        return InstanceHolder.instance();
      }
    }
  • 优点:线程安全,资源利用率高。
  • 缺点:第一次加载时反应不快。
  • 原理:类级内部类(static修饰的成员内部类)只有在第一次使用时才会被加载

Summary

  • 考虑到效率、安全性等问题,一般常用饿汉式singleton or static内部类singleton。其中后者是常用的singleton实现方法。

 

[Java] [Singleton] [DCL]

标签:懒汉   指令重排序   意思   single   操作   自己的   原子操作   col   可见性   

原文地址:http://www.cnblogs.com/wttttt/p/7765076.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!