如果我们仅在双重检查单例模式中使用外部null检查,该怎么办?

时间:2019-07-05 22:44:34

标签: java multithreading singleton synchronized synchronized-block

问题1:为什么在单例模式的多线程中我们需要两个null检查?如果我们仅使用外部支票怎么办?

    if (instance == null) {
        synchronized (ABC.class) {

            // What if we remove this check?
            if (instance == null) {
                instance = new ABC();
            }
    }

问题2:以下内容有什么区别?

1:直接在synced()内部使用类名

    public ABC getInstance() {
        if (instance == null) {
            // Difference here
            synchronized (ABC.class) {
                if (instance == null) {
                    instance = new ABC();
                }
            }
         }
         return instance;
    }

2:在synced()中使用静态最终对象

    private static final Object LOCK = new Object();
    .
    .
    public ABC getInstance() {
        if (instance == null) {

             // Difference here
             synchronized (LOCK) {
                if (instance == null) {
                    instance = new ABC();
                }
             }
         }
         return instance;
    }

3:在synced()内部使用 new Object()

    if (instance == null) {
    // Difference here
         synchronized (new Object()) {
            if (instance == null) {
                instance = new ABC();
            }
        }
     }

1 个答案:

答案 0 :(得分:-1)

  1. 删除内部nullcheck可能会导致争用情况。想象一下以下情形:两个线程尝试在同一时间获取对象的实例,因此它们都检查instance是否等于null并获得正确的答案,因此两个线程都将尝试创建对象的实例。因为此代码是同步的,所以这些线程中的一个将进入同步的代码块,而另一个则等待释放锁。当第一个线程实例化并返回对象的实例后,该锁将被释放,第二个线程将执行同步块,因此它将创建一个新实例并返回它,因为它不知道它是先前创建的在等待轮到的时候。
  2. 在同步中将类用作参数将生成一个静态锁。这意味着该类的所有实例将共享锁。
  3. 如果要使用特定对象而非类或此对象锁定同步块,则将对象用作同步参数非常有用。这使您可以在同一类中使用不同的锁来拥有不同的代码块。例如:

    Object o1 = new Object();
    Object o2 = new Object();
    synchronized(o1) {
        //your synchronized block
     }
    
    synchronized(o2){
        //other synchronized block
    }
    

在先前的代码示例中,block1和block2可以由不同的线程同时执行,因为它们使用了不同的锁对象。如果您对两个代码块(即类)使用相同的锁,则块1将被阻塞,直到块2完成其执行为止,反之亦然。