交叉同步块

时间:2013-09-27 13:25:48

标签: java multithreading

我有从ThreadB向线程ThreadA发送通知的功能,并等待ThreadA返回ThreadB的通知。

A通知B然后B通知A ThreadA读取ThreadB数据并在完成发送通知时到ThreadB

sendNotifyAndWaitNotify在ThreadB中运行。

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

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

我希望在退出同步块#1而不是#2后将唤醒ThreadA。我希望块#2中的代码可能第二个块在不同的同步对象上做得更好?

我必须使用java 1.4。

2 个答案:

答案 0 :(得分:1)

您应始终使用循环条件保护对wait()的呼叫。

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

这样一来,如果你的条件已经在线程到达同步块的时候满足,它就不会等待任何事情。

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

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

摘录(强调我的):

  

线程也可以在没有被通知,中断或者被唤醒的情况下被唤醒   超时,即所谓的虚假唤醒。虽然这很少发生   在实践中,应用程序必须通过测试来防范它   应该导致线程被唤醒的条件,以及   如果条件不满意,继续等待。换一种说法,   等待应始终以循环方式发生,如下所示:

 synchronized (obj) {
     while (<condition does not hold>)
         obj.wait(timeout);
     ... // Perform action appropriate to condition
 }

答案 1 :(得分: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.