嵌套同步块的缺点

Disadvantages of nested synchronized blocks

学习线程和并发。考虑以下代码:

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 的以下子类:

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);
    }
  }

}

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

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

Is update2 the correct way to implement a thread-safe update method?

是的,是的。您已经实现了原子性,并且与个别 addremove 方法的调用者兼容。

Are there any disadvantages of having nested synchronized blocks over the same lock?

否,因为锁是 可重入的,这意味着第二次获取除了记住锁已被再次获取外什么都不做,所以它不会被释放直到执行了两个 release 操作。

使用synchronized使得整个对象"locked": 没有其他线程可以调用任何方法,而另一个线程是运行对象同步方法之一。

这种行为确实会使某些程序变慢。

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

下面是如何应用 post 中的代码:

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();
     }
  }
}

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

重入锁是即使同一个线程已经获得了锁,也可以重新获取的锁。

为什么你不在 Super class 方法中提供 removeAndAdd ,如果有一个场景必须有 remove 和 add atomic 。

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