接着说我们的单例故事。 想要解决上面的线程不安全问题,有很多种办法,这里我们一一来探讨。 第一种就是通过synchronized来控制线程安全,将synchronized加在getInstance方法上, public static synchronized SingleFactory getInstance() 运行下之前的main方法,是不是发现hashCode都一样了?开心吧,这么严重的问题一下子就解决了。 ~~~~ 再来一盆冷水: 这种做法其实效率很低。因为在任何时候只能有一个线程调用该方法,其余的需要加入等待,阻塞程度可想而知,如何解决这个问题呢? 我们来分析下,一般这种单例模式都会用在什么场合?是不是初始化的时候? 好的,我们姑且就认为是初始化的时候,即第一次调用的时候创建对象,后续只存在调用的操作。那么就印出来我们之前提到的双重检验 锁。 双重检验锁 双重检验锁(double checked locking pattern),是一种使用同步块加锁的方法。之所以称之为双重检验锁,是 因为会有两次检验 instance == null 的操作。一次是在同步块外面,一次是在里面。 先来上代码: public static SingleFactory getInstance() { if (instance == null) { synchronized (SingleFactory.class) { if (instance == null) { instance = new SingleFactory(); } } } return instance; }
运行后是不是发现也ok? 好了,我们来说一下为什么要在同步块外面一次,里面又来一次。 因为当我们第一次初始化的时候,可能会有多个线程同时进入外部的if判断中,如果这个时候没有对当前类进行同步块加锁,则很容易就出现多 个实例的状态。因此我们需要在外面做次判断,然后判断里面加上一个同步块。 ~~~~ 再来一盆冷水。 为啥?上面的代码看起来很严谨,很完美啊,为什么还是不对?
|