等待/通知死锁

时间:2011-12-15 11:41:26

标签: java multithreading deadlock wait

我在“Add”和“Get”方法中有一个带有一些阻塞机制的队列,其中第一个线程添加数据,第二个线程获取数据。

public synchronized MyObj getData() {               
    synchronized (myLock) {
        synchronized (this) {
            if (isEmpty()) {                
                wait(0);                    
            }
        }       


        return getData();           
    }
}

public synchronized void addData(MyObj data) {
    if (!isFull()) {
        putData(data);
        synchronized (this) {
            notify();
        }
    }
}

在上面的代码中,如果第一个线程尝试获取数据并且队列为空,则通过wait(0)等待,直到其他线程添加数据以通过notify()等待释放等待释放。

现在我想在队列已满时添加另一个“锁定”,并且有人试图向其中添加更多数据:

public synchronized MyObj getData() {               
    synchronized (myLock) {
        synchronized (this) {
            if (isEmpty()) {                
                wait(0);                    
            }
        }       

        synchronized (this) {
            notify();
        }
        return getData();           
    }
}

public synchronized void addData(MyObj data) {
    synchronized (myLock) {
        synchronized (this) {
            if (isFull()) {
                wait(0);
            }
        }
    }

    synchronized (this) {
        notify();
        }
        PutData(data);
}

结果不是我所期望的,我想我得到一个死锁原因过程被卡住了。

更新

这是我获取数据的方式:

queueSize--;
startPointer = (startPointer + 1) % mqueueSize;
data = (String) queue[startPointer];

这是我添加数据的方式

  queueSize++;
  endPointer = (endPointer + 1) % mqueueSize;
  queue[endPointer] = data;

public synchronized boolean isEmpty() {
        return queueSize== 0;
    }

    public synchronized boolean isFull() {
        return queueSize== mqueueSize;
    }

5 个答案:

答案 0 :(得分:3)

为什么你有三个synchronized陈述? wait(0)仅释放this上的锁定,因此只需保留该锁定并转储方法中的synchronizedsynchronized(myLock)

每当你在某个对象上调用wait(在这种情况下你调用this)时,该对象的锁定会自动释放,以允许其他线程继续。但是你永远不会在myLock上等待(也不应该,因为你已经在呼叫this)。那部分是多余的,导致死锁。

考虑这种情况:应该添加的线程获取myLock上的锁,但发现队列已满,因此等待。此等待不会释放myLock上的锁定。另一个线程想要获取数据但不能进入synchronized块,因为第一个线程没有释放myLock上的锁。

结论:删除synchronized(myLock)块。

答案 1 :(得分:1)

为什么你不看java.util.BlockingQueue。可能它会对你的情况有用。

特别看一下java.util.LinkedBlockingQueue,如果你在构造函数中指定了队列的容量,那么队列就会阻塞。

答案 2 :(得分:1)

从方法签名中删除synchronized关键字,因为这意味着您为整个方法调用保留this监视器 - synchronized(this)块只是多余的。

编辑:

...然后拨打等待并通知myLock而不是this。完全忘记this上的同步。这是因为在等待(在当前代码中this上)时,您没有释放myLock锁定,因此其他线程无法转到notify()

答案 3 :(得分:0)

if替换为while。如果收集真的变得不空/不满,那么仔细检查也不会有什么坏处。

你真的不需要两个锁。单锁几乎可以工作,应该更简单。

public synchronized T get()
{
    while(isEmpty())
        wait(0);

    notifyAll();

    return super.get();

}

public synchronized put(T t)
{

    while(isFull())
        wait(0);

    super.put(t);

    notifyAll();

}

当某些内容发生变化时,所有线程都会被唤醒。但如果他们无法完成工作,他们将wait用于下一个notify

答案 4 :(得分:0)

如前所述,您的代码有太多synchronized个注释。此外,在wait条件中检查if开启的条件,但理想情况下应在while循环中检查以避免spurious wakeups。以下是修复这些内容的代码大纲。

// _isEmpty and _getData are private unsynchronized methods
public MyData get() throws InterruptedException {
  // wait and notify should be called from a block
  // synchronized on the same lock object (here myLock)       
  synchronized (myLock) {
    // the condition should be tested in a while loop
    // to avoid issues with spurious wakeups
    while (_isEmpty()) {
      // releases the lock and wait for a notify to be called
      myLock.wait();
    }
    // when control reaches here, we know for sure that
    // the queue is not empty
    MyData data = _getData();
    // try to wake up all waiting threads - maybe some thread
    // is waiting for the queue not to be full
    myLock.notifyAll();
  }
}

// _isFull and _putData are private unsynchronized methods
public void put(MyData obj) throws InterruptedException {
  synchronized (myLock) {
    while (_isFull()) {
      myLock.wait();
    }
    _putData(obj);
    myLock.notifyAll();
  }
}