【问题标题】:Java concurrency: synchronized method and synchronized blockJava并发:同步方法和同步块
【发布时间】:2022-12-10 10:54:06
【问题描述】:

我有两种方法可以从循环缓冲区中添加和删除元素

第一个实现:

synchronized void add(byte b) throws InterruptedException {

  if(availableObjects == size) wait();
  buffer[(tail+1)%size] = b ;
  tail++;
  availableObjects++;
  notifyAll();
}

synchronized byte remove() throws InterruptedException {

  if(head==tail) wait();
  Byte element = buffer[head%size];
  head ++ ;
  availableObjects--;
  notifyAll();
  return element;

}

和第二个实现:

private final Object addLock= new Object ();
private final Object removeLock=new Object ();

void add (byte b) throws InterruptedException{
    synchronized (addLock){
        while (availaibleObjects.get () == size) addLock.wait();
        buffer [tail]= b;
        tail = [tail + 1) % size;
        availaibleObjects.incrementAndGet();}

    synchronized (removeLock){ // why we added this block ? 
        removeLock.notifyAll();}
    }


 byte remove () throws InterruptedException{
    byte element;
    synchronized (removeLock){
        while (availaibleObjects.get () == 0) removeLock.wait() ;
         element = buffer[head] ;
         head=(head + 1) % size;
         availaibleObjects.decrementAndGet();}

        synchronized (addLock){ // why we added this block ? 
            addLock.notifyAll();}
            return element;}

我的问题是为什么在方法的第二次实现中我们添加了第二个同步块?

  1. 从第一个实现中我了解到两个线程不能同时添加和删除。
  2. 从第二个实现开始,两个线程可以同时添加和删除,但我不明白为什么要添加这些块:
    synchronized (removeLock){ // why we added this block ? 
            removeLock.notifyAll();}
    
    
     synchronized (addLock){ // why we added this block ? 
                addLock.notifyAll();}
                return element;}
    
    
    
    
    

【问题讨论】:

  • 代码必须位于 synchonized 块中才能执行 notifyAll()wait 调用。
  • 我猜想所有的变异操作(即添加和删除)应该共享同一个锁。我无法想象同时执行添加和删除是安全的。
  • @K.Nicholas 是的,我明白了,但为什么我们将 notifyAll() 添加到同步块中?我们可以在第一个街区做到这一点
  • 同步块特定于对象。 RTFM。
  • @K.Nicholas 明白了,非常感谢。

标签: java multithreading concurrency locking


【解决方案1】:

我的问题是为什么在方法的第二次实现中我们添加了第二个同步块?

每当您在对象上调用wait()notify() 时,您必须在该特定对象的synchronized 块中。所以如果你想打电话给addLock.notifyAll();,你必须在synchronized (addLock)里面。 synchronized 关键字提供锁定和内存同步。

警告:更重要的是,您有一个错误,因为您在 addLock 同步块内写入 buffer,但在 removeLock 内读取。作者可以在addLock 内,添加项目,并增加availableObjects 计数器,而读者已经在addLock 内。这意味着读者可能不是查看对 buffer 的更新,并可能将元素读取为 null。如果你正在改变buffer,然后稍后从中读取,两者都应该保存在同一个对象的synchronized块中——你也可以锁定buffer。通过此更改,您可以将 availableObjects 降级为普通的 integer

addLock.notifyAll();

如果你这样做正确,你应该能够调用notify()而不是notifyAll(),因为从缓冲区添加或删除的每个元素最多将释放 1 个等待线程。

if(availableObjects == size) wait();

重要的是要指出您的第二个实现是正确的,可以将这些从 if 更改为 while 语句。它需要在 while 循环中进行测试,否则您将遇到竞争条件和多个发布者和订阅者的错误——在某些架构上也会出现虚假唤醒。见my page on these race conditions

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-12
    • 1970-01-01
    • 2011-12-01
    相关资源
    最近更新 更多