单例模式目的是在一个进程中只有一个实例,具体实现是将构造方法私有化,在类的内部使用一个静态字段来引用唯一创建的实例
单例模式分为以下几种模式
饿汉式:
1 | public class Singleton { |
缺点:
单例对象的创建不是延迟加载
优点:
线程安全,不需要加锁
懒汉式
懒汉式为了支持延迟加载,将对象的创建延迟到了获得对象的时候。但是为了线程安全不得不为了获取对象的操作加锁,这降低了性能。并且这个锁只有第一次创建对象时候有用,之后就是累赘
1 | public class Singleton { |
优点
创建对象的过程是线程安全的,并且支持延迟加载
缺点
加锁影响了性能
双重检测
双重检测解决了懒汉式和饿汉式的缺点,就是将原有的锁住整个方法改成synchronized
代码块,同时使用volatile
来禁止重排序,保证了多线程下的线程安全
1 | public class Singleton { |
如果不加volatile
的话,在第二个判空的位置可能有问题
因为在instance不为空时候,可能此时这个对象还没有完成初始化
创建对象这个过程,大致可以分为三步
1、分配对象的内存空间
2、初始化对象
3、设置instance指向刚分配的内存地址
第二步和第三步可能会发生重排序,但是单线程会遵循intra-thread semantics
即保证重排序不会改变单线程的程序执行结果。但是在多线程的情况下,另一个线程可能看到一个未初始化的对象,这时候直接返回会出错。
当变量声明为volatile
后,多线程环境中的第二步和第三步重排序会被禁止。
优缺点
优点
- 保证一个类只有一个实例
- 获得了一个指向该实例的全局访问节点
- 仅在首次请求单例对象时候完成初始化
缺点
- 违反了单一职责原则,同时解决了两个问题
- 多线程环境需要额外处理