转自:https://blog.youkuaiyun.com/u011386173/article/details/82454714
1.饿汉式
(1)饿汉式
提前(类加载时)创建
- 优点:确保单例;
- 缺点:单例永远不用时浪费内存
- 常见方式
public class Singleton {
private static Singleton instance=new Singleton();//类加载时执行
private Singleton(){}//非私有构造器无法保证单例
public static Singleton getInstance(){
return instance;
}
}
(2)饿汉式变体
public class Singleton {
private static Singleton instance=null;
static {//静态代码块类加载时执行
instance=new Singleton();
}
private Singleton(){}//非私有构造器无法保证单例
public static Singleton getInstance(){
return instance;
}
}
2.懒汉式
(1)懒汉式(非线程安全)
- 优点:需要时创建;
- 缺点:非线程安全,无法保证单例
- 不推荐
public class Singleton {
private static Singleton instance=null;
private Singleton(){}//非私有构造器无法保证单例
public static Singleton getInstance(){
if(instance==null) {
instance=new Singleton();
}
return instance;
}
}
(2)懒汉式(线程安全)
- 优点:需要时创建;
- 缺点:多线程时需要等待,效率低
- 不推荐
public class Singleton {
private static Singleton instance=null;
private Singleton(){}//非私有构造器无法保证单例
public static synchronized Singleton getInstance(){
if(instance==null) {
instance=new Singleton();
}
return instance;
}
}
3.静态内部类
类似于饿汉式
public class Singleton {
private Singleton(){}//非私有构造器无法保证单例
private static class SingletonHolder {
private static Singleton instance=new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
4.双重检查锁(DCL)
属于懒汉式
- 优点:线程安全,保证单例,效率高;需要时创建;
- 推荐使用
public class Singleton {
private volatile static Singleton instance;//volatile作用禁止指令重排
private Singleton(){}
public static Singleton getInstance(){
if(instance==null){//只有前面先执行的几个线程(不包括第一个获得锁的线程)会阻塞,保证效率
synchronized (Singleton.class){//加锁,保证线程安全
if(instance==null){//保证单例
instance=new Singleton();//非原子操作:1分配堆内存-2初始化实例-3地址赋值给引用
}
}
}
return instance;
}
}
两次判空和加锁很容易理解,现在详细说说volatile作用禁止指令重排的作用:
现假设没使用volatile关键字,instance=new Singleton();
不是原子操作,可以分为下面三个指令:
- 1.为对象分配堆内存;
- 2.初始化对象;
- 3.将分配的内存空间的首地址赋值给instance引用;
多线程操作时,没有依赖关系的指令可能会进行指令重排优化;假设第一个获得锁的线程按1-3-2的顺序执行指令;在执行完3的时候,instance!=null
,此时正好有另外一个线程正在执行第一次判空操作然后返回这个引用,但此时单例对象还没有初始化完成,如果用这个引用进行一些操作会出现问题。当然使用了volatile禁止指令重排,就一定会按1-2-3的顺序执行,返回的引用一是已经实例化的对象引用,不会出现问题。
5.枚举
- 优点:代码简单,支持序列化
- 缺点: 不能需要时创建(懒加载)
public enum Singleton {
INSTANCE;
public void whateverMethod(){
}
}
最后借用 《Effective Java》一书中的话:
单元素的枚举类型已经成为实现Singleton的最佳方法.
单例模式在Android使用场景:
xxxManager , xxxHelper , xxxUtils