2

我有将通知发送ThreadB到线程ThreadA并等待通知从ThreadA返回到ThreadB.

在这期间,由读取数据A通知B,然后BA ThreadA读取ThreadB数据通知,并在完成时向 发送通知ThreadB

sendNotifyAndWaitNotify 在 ThreadB 中运行。

在同一个对象上逐一同步可能看起来很奇怪,但B.sync.notify();只有在它退出同步块时才会发送通知,如果我将在一个块中写入所有内容,我将首先等待,只有在退出同步块时才会发出通知。这是不可接受的。

void sendNotifyAndWaitNotify() {
    synchronized(B.sync) {
        B.sync.notify();
    }
    //ThreadA reads ThreadB data
    synchronized (B.sync) {     
        try {
            debug("start waiting for notify");
            B.sync.wait();
        } catch (Exception e) {
            error( e.toString());
        }
    }
    debug("reader sleep done");
}

但是这个函数是不正确的,因为ThreadA可以在ThreadB没有启动等待的那一刻读取数据并通知ThreadB。是否可以通过交叉同步块来解决问题,如下所示:

void sendNotifyAndWaitNotify() 
   {
   synchronized(B.sync) // block #1
      {
      synchronized(B.sync)  // block #2
         {
         B.sync.notify();
         }

   //ThreadA reads ThreadB data

      try 
         {
         debug("start waiting for notify");
         B.sync.wait();
         } catch (Exception e) {
         error( e.toString());
         }
      debug("reader sleep done");
      }
   }

我希望 ThreadA 在退出同步块 #1 而不是 #2 后会被唤醒。而且我希望块#2中的代码也许第二个块更好地用于不同的同步对象?

我必须使用 java 1.4。

4

2 回答 2

1

wait()您应该始终保护带有循环条件的调用。

synchronized(B.sync){
    while(!myConditionIsSatisfied){
        B.sync.wait();
    }
    /* Do critical stuff */
}

这样,如果您的条件在线程到达同步块时已经满足,它将不会等待任何东西。

用循环条件保护等待的另一个原因是它的合约允许虚假wait()唤醒。如果发生这种情况,如果您的条件不满足,您希望将线程发送回。wait()

另请参阅:Object.wait(long) 的 JavaDoc Object.wait() 是对 Object.wait(0) 的调用

摘录(强调我的):

线程也可以在没有被通知、中断或超时的情况下唤醒,即所谓的虚假唤醒。虽然这在实践中很少发生,但应用程序必须通过测试应该导致线程被唤醒的条件来防范它,如果条件不满足则继续等待。换句话说, 等待应该总是发生在循环中,就像这样:

 synchronized (obj) {
     while (<condition does not hold>)
         obj.wait(timeout);
     ... // Perform action appropriate to condition
 }
于 2013-09-27T13:45:36.257 回答
0

试试下面的。使用两个监视器和布尔标志来指示线程是否必须等待。这将保护您免受虚假唤醒,并且您不会丢失通知。

您也可以只使用一台显示器,但在我看来,这更具可读性。

如果线程 A 在监视器上调用 notify 并且线程 B 尚未处于等待状态,则程序将永远停止。所以测试你是否需要等待。

public class Testnotify {

    private final Object monitorA = new Object();
    private boolean finishedA = false;

    private final Object monitorB = new Object();
    private boolean finishedB = false;

    private void threadAwaitAndNotify() throws InterruptedException {
        // do some stuff

        // notify the other thread that we are finished here
        synchronized (monitorA) {
            finishedA = true;
            System.out.println(Thread.currentThread().getName()
                    + " Notify thread B.");
            monitorA.notifyAll();
        }

        // now wait for the other thread to finish
        synchronized (monitorB) {
            while (!finishedB) {
                System.out.println(Thread.currentThread().getName()
                        + " Waiting for thread B.");
                monitorB.wait();
            }
            System.out.println(Thread.currentThread().getName()
                    + " Thread B ready.");
        }
    }

    private void threadBwaitAndNotify() throws InterruptedException {
        synchronized (monitorA) {
            while (!finishedA) {
                System.out.println(Thread.currentThread().getName()
                        + " Waiting for thread A.");
                monitorA.wait();
            }
            System.out.println(Thread.currentThread().getName()
                    + " Thread A ready.");
        }

        synchronized (monitorB) {
            finishedB = true;
            System.out.println(Thread.currentThread().getName()
                    + " Notify thread A.");
            monitorB.notifyAll();
        }
    }

    public static void main(String[] args) {
        final Testnotify testnotify = new Testnotify();

        final Thread threadA = new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    testnotify.threadAwaitAndNotify();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }, "A");

        final Thread threadB = new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    testnotify.threadBwaitAndNotify();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }, "B");

        threadA.start();
        threadB.start();

    }

}

会给你输出:

B Waiting for thread A.
A Notify thread B.
A Waiting for thread B.
B Thread A ready.
B Notify thread A.
A Thread B ready.
于 2013-09-27T15:25:43.130 回答