单例模式 (懒汉式, 线程同步详解)

来源:互联网 发布:樱井翔堀北真希 知乎 编辑:程序博客网 时间:2024/05/18 00:57

单例模式(懒汉式)

在懒汉式写法中, 我们需要非常注意线程同步的问题. 大概有一下几个:
1. getInstance() 直接锁方法好不好
2. 双重锁定
3. synchronized(this)行不行

1. getInstance() 直接锁方法好不好

这种写法:

class Singleton {    private static Singleton instance;    private Singleton(){}    public static synchronized Singleton getInstance(){        if(instance == null){            instance = new Singleton();        }        return instance;    }}

我们可以看到这里synchronized是锁方法, 当两个线程都要进入getInstance()时, 只有一个能进入, 并创建出实例, 然后另外一个进入后, 判断 instace不为null, 然后直接得到instance. 这种做法是没有错误的. 但是由于线程都需要通过getInstance()来获取对象, 所以getInstance()调用频率很高, 所以线程被锁的频率也很高, 所以这种做法效率低.

2. 双重锁定

由于上面效率的原因, 你可能就会想到把 syschronized 放在 getInstance()里面, 这种可避免在调用getInstance()时的阻塞问题, 如下:

class Singleton {    private static Singleton instance;    private Singleton(){}    public static Singleton getInstance(){        if(instance == null){            synchronized(Singleton.class){                instance = new Singleton();            }        }        return instance;    }}

这种写法看似没有问题, 其实却有一个很大的隐患, 在于: 如果两个线程同时执行getInstance(),判断 instance都不为null后, 进入if判断语句. 这个时候一个线程获得锁, 然后进入new了一个对象, 并开心的执行完了. 这个时候另外一个线程获得了锁, 但让它也不会再去判断 instace是否为null, 所以它也会再执行一次new操作. 所以这里执行了两次new操作. 当然最后instance还是只指向后一次new的对象.
所以这个时候需要双重锁定, 就是在 synchronized中再加一次 null判断, 如下:

class Singleton {    private static Singleton instance;    private Singleton(){}    public static Singleton getInstance(){        if(instance == null){            synchronized(Singleton.class){                if(instance == null){                    instance = new Singleton();                }            }        }        return instance;    }}

这样就可以保证不会new两次, 也是相对比较正确的, 并且效率也很高.

3. synchronized(this)行不行

答案是不行的, 如果你写代码看一看, 直接就提示语法错误了, 因为我们的 getInstance() 方法是 static的, 所以里面不能使用 this.

好了以上三点终结, 希望对大家有帮助.

0 0