您的位置:首页 > 其它

线程安全的单例模式是否真的安全(2)

2017-09-17 19:33 239 查看
在上一篇《线程安全的单例模式是否真的安全》的博文中我们交流了关于常见的双重检查锁定的单例模式存在的问题,并给出了第一种关于使用volitaile解决方式,,今天我们来说第二种解决方式。
闲话不多说,我们直接上代码:


public class InstanceCreater {
private static class InstanceProducter {
public static Instance instance = new Instance();
}
}

public static Instance getInstance() {
return InstanceCreater.InstanceProducter.instance;
}
}


在虚拟机初始化类的时候,我们会去获取一个类初始化锁,这个锁对象可以同步对类进行初始化的线程,什么意思呢,可以这样理解,当一个线程需要使用类的方法或者对象的时候,这个线程就会去初始化类,但是类初始化一次就可以了,那么我们就必须让先承认那个知道类已经被初始化过了,怎么让线程知道类被初始化了呢,很简单,设置一个变量来标记一下。每个类都会有一个变量state,这个变量会有三个值,分别是noInitialization,initializating,initializated,分别标志着类未初始化,类初始化中,类已经初始化完毕。

那上面说的这些又和单例模式有什么关系呢,各位看官不要急。

说完了累的初始化状态我们再来说一下类在什么情况下,会被初始化:

1.当一个类被创建对象的时候,会被初始化。

2.当一个类中的静态方法被使用时,会被初始化。

3.当一个类中的静态变量被赋值时,会进行初始化。

4.当一个类中的静态变量被使用,而这个变量不是一个常亮的时候会被初始化。

很明显,上面的代码属于第四种情况。

那么在类的初始化过程中,如果多个线程来初始化,又是怎么保持同步的呢?

首先当A线程来初始化类的时候,读取到的state状态为noInitialization,所以就需要进行初始化,在初始化过程中,将state变成了initializating初始化中。然后对类初始化锁进行加锁,进行初始化。当线程B来要求使用类,并初始化类的时候,读取到的state为initializating,所以就会知道已经有线程在初始化类,那么线程就会进行等待,之后所有需要初始化类的线程都会像B一样,进行等待。当A线程初始化完成时,将state改为initializated初始化已经完成,在A释放类初始化锁之前,通知等待的线程来使用类,那么所有等待的线程就可以拿到初始化好的类进行使用,也就是上面的getInstance()方法,也就拿到了instance的唯一实例,当线程C来访问时,读取到的状态为initializated,就不再会进行初始化,然后调用getInstance方法的时候,由于InstanceProducter类已经初始化完成,所以得到的就是一个唯一实例。

好了,这就是解决双重检查锁定的到单例模式的第二种方法,如果有不适当的地方,希望大家多多指教批评。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: