嵌套同步块的缺点

时间:2015-02-04 16:52:16

标签: java multithreading synchronization synchronized

了解线程和并发性。请考虑以下代码:

class A {

  protected final Object lock = new Object();

  public void remove(Object obj){
    synchronized(lock){
      // remove the object
    }
  }

  public void add(Object obj){
    synchronized(lock){
      // add the object
    }
  }
}

这个代码是线程安全的,因为当一个线程处于添加或删除过程中时,没有两个不同的线程可以addremove

现在考虑A:

的以下sublcass
class B extends A {

  public void update1(Object original, Object newObj){
    remove(original);
    add(original); 
  }

  public void update2(Object original, Object newObj){
    synchronized(lock) {
      remove(original);
      add(newObj);
    }
  }

}

B必须实现线程安全的update方法。现在,据我所知update1不是线程安全的,因为操作不是原子的,即removeadd的执行之间没有同步(如果错误则纠正我)。

update2是实现线程安全update方法的正确方法吗? 在同一lock上嵌套同步块是否有任何缺点?

3 个答案:

答案 0 :(得分:7)

  

update2是实现线程安全更新方法的正确方法吗?

是的,确实如此。您已达到原子性,并且与单个addremove方法的来电者兼容。

  

在同一个锁上嵌套同步块有什么缺点吗?

不,因为锁是可重入,这意味着第二次获取不会再记住再次获取锁定,所以它不会被释放,直到执行两个发布操作。

答案 1 :(得分:0)

synchronized的使用使整个对象“锁定”:当另一个线程运行其中一个对象同步方法时,没有其他线程可以调用任何方法。

这种行为确实会减慢一些程序。

作为替代方案,您可以使用ReentrantLock

以下是它如何适用于帖子中的代码:

class A {
  protected final Lock lock = new ReentrantLock();

  public void remove(Object obj){
    lock.lock();
    try {
      // remove the object
    } finally {
       lock.unlock();
    }
  }

  public void add(Object obj){
    lock.lock();
    try {
      // add the object
    } finally {
       lock.unlock();
    }
  }
}

class B extends A {
  // ...

  public void update2(Object original, Object newObj){
     lock.lock();
     try {
        remove(original);
        add(newObj);
     } finally {
       lock.unlock();
     }
  }
}

答案 2 :(得分:0)

锁是重新进入的,除非你在每个地方看到死锁

重入锁定即使锁定已经由同一个线程获得也可以获得锁定。

如果存在必须删除并添加原子的场景,为什么不在超类方法中提供removeAndAdd。

出于安全原因,请确保您的锁定对象标记为私有和最终。