确保某一个类只有一个实例,并且自行实例化并向整个系统提供这个实例java
确保某一个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对象只应该有且只有一个。例如要访问IO和数据库等资源,这时候就能够考虑使用单列模式。数据库
classDiagram Singleton <.. Client class Singleton{ +getInstanc()Singleton -Singleton() }
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;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
复制代码
这种方式的优势是只有在第一次使用的时候才会被实例化,能够看到 getInstance() 添加了 synchronized 修饰,它是一个同步方法,这是为了在多线程的状况下保证单列对象的惟一性,若是只在单线程使用能够不加。也是由于添加了 synchronized 致使每次调用getInstance()都须要进行同步,致使没必要要的同步开销,因此不建议使用这种写法。markdown
public class Singleton{
private volatile static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
synchronized(Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
复制代码
这种方式相比上面的避免了不必的同步,同时也能作到线程安全。能够看到有两次判空,第一次试为了不没必要要的同步,第二次是为了确保单列对象的惟一性。这里仍是用了 volatile 关键字,若是不用就有可能出现DCL 失效的问题。多线程
是什么致使DCL 失效的?咱们接着往下看。函数
假设A线程执行到 instance = new Singleton() 这行代码,这行代码最终会被编译成多条指令,大体作如下3键事情:性能
可是因为Java 编译器容许处理器乱序执行,这时候就有可能出现执行顺序为1-3-2,当A线程执行完3还未执行2的时候,B线程取走了instance ,在使用时就会出错,这就是DCL 失效问题。而使用volatile 关键字修饰能够禁止进行指令重排序,全部能够有效的避免这个问题。固然使用volatile 也会对性能有一点影响,但考虑程序的正确性,这点牺牲是值得的。spa
public class Singleton{
private Singleton(){}
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
}
复制代码
由于静态内部类不会由于外部类的加载而加载,静态内部类加载不须要依附于外部类,但在加载静态内部类时必定会加载外部类。线程
所以使用这种方式能够确保线程安全,也能保证单列对象的惟一性,同时也延迟了单列的实例化,也不会有性能影响。因此这是最推荐的使用方式。code
public enmu Singleton{
INSTANCE;
public void doSomething(){
}
}
复制代码
枚举默认是线程安全的并且反序列化也不会致使从新建立对象,保证单列对象的惟一性。若是以前4种模式要杜绝反序列化时从新生成对象,那么必须加入 readResolve() 函数。
public class Singleton implements Serializable{
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
private Object readResolve() throws ObjectStreamException{
return instance;
}
}
复制代码
以上介绍了单列的5种实现方式,并不是所有。就我的而言,不考虑懒加载的状况下使用 饿汉模式 便可,不然建议使用 静态内部类模式,若是须要考虑反序列化的状况能够考虑 枚举模式 ,枚举模式由于可读性不太好,因此通常用的比较少。