单例模式,是设计模式的一种。具体实现细节为,某一个类,只对外提供同一个对象的实例。
特点:1.私有的构造方法(外界无法new出对象)
2.开放一个方法,返回类型为该单例对象
实现方式: 1.饿汉模式:new对象操作在类加载的时候就执行。也就是说, 即使我没有要获取这个对象,这个对象也已经被new出来了。
public class Singleton {
//饿汉模式
private static Singleton instance = new Singleton();
private Singleton(){}
public Singleton getInstance(){
return instance;
}
}
优点:线程安全。
缺点:占用不必要的空间(有时候不急着使用,但已经被new出来了),并且,如果在类加载的时候抛出异常,那么new出来的这个对象永久性损坏,不能再使用了。
2.懒汉模式
private static Singleton instance =null;
private Singleton(){}
public Singleton getInstance(){
if(instance==null){
instance = new Singleton();
}
return instance;
}
类加载时不初始化对象,在需要得到该对象时才初始化。该模式线程不安全
3.懒汉式+线程安全
private static Singleton instance =null;
private Singleton(){}
public synchronized Singleton getInstance(){
if(instance==null){
instance = new Singleton();
}
return instance;
}
缺点:即使已经初始化完毕,依旧不能并发并行执行,因为有锁的存在,效率比较低
4.双重校验锁
private static volatile Singleton instance =null;
private Singleton(){}
public Singleton getInstance(){
if(instance==null){
synchronized (Singleton.class){
if(instance==null){
instance = new Singleton();
}
}
}
return instance;
}
完美的解决了上述问题。加上volatile的目的是保证第一个if读的线程安全性
那么指令重排序会对安全性造成什么影响呢?
instance = new Singleton();
来看上面这行代码。对象的初始化,具体来说可以拆分为三个部分
1.分配对象内存空间
2.初始化对象
3.赋值给引用
而JVM在执行过程中为了保证效率等因素可能会进行重排序,如顺序变为132。这样操作对该线程本身是不会产生影响的,但如果在13之后,进入另一个线程,该线程判断instance将不为null(因为该引用已经指向了一块内存空间),就会导致异常。因此,加入volatile的另一个作用是为了禁止指令重排序
具体来说,volatile建立了一个内存屏障,屏障前的指令不能排序到屏障后,屏障后的指令不能排序到屏障前。