单例模式
class SingleInstance {
private static SingleInstance instance;
private SingleInstance() {
}
public static SingleInstance getInstance() {
if (instance == null) { (i)
synchronized (SingleInstance.class) {
if (instance == null) {
instance = new SingleInstance(); (j)
}
}
}
return instance; (k)
}
}
1、不加任何同步锁的时候,当有两个线程同时执行到(j)处时,会创建两个instance实例对象。
2、双重检查也不是绝对安全的,因为JVM编译器可能对执行进行重排,new一个对象时不是原子操作。
一个对象的初始化正常顺序:
(1)memory=allocate();//分配对象的内存空间
(2)ctorinstance(memory)//初始化对象
(3)设置栈上的引用指向刚分配的内存地址。
但是重排后可能会出现:
(1)memory=allocate();//分配对象的内存空间
(3)设置栈上的引用指向刚分配的内存地址。
(2)ctorinstance(memory)//初始化对象。
如果线程A执行到(i)处,线程B执行到(j)处,并且线程B执行了重排列后的指令(1)(3),这时A线程抢到CPU资源,判空时,发现instance不为空,就直接返回(K)处的instance对象,由于这个对象是未初始化过的对象,那么在用该对象调用实例方法时,就会出现程序异常。
3、双重检查加锁“的方式可以既实现线程安全(不保证绝对安全),又能够使性能不受到很大的影响。
双重检测机制在获取对象实例的时候,先判空,如果对象实例为空时才同步,不为空时直接返 回对象,并不是像单层的那样每次获取都要同步,这样减少了每次进入都要同步的时间。
4、加了双重检测机制还可能不安全,那么可以在声明对象的前面加一个volatitle关键字
这个关键字可以可以防止指令重排,也可以保证线程访问的变量值是主内存中的最新值。那么在双重检测的基础上,就能够保证线程的安全。