我有从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。
答案 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.