Java 奇怪的种族状况?
我写的方法有问题。它所做的是等待一个属性等于另一个值。我不能在这里复制它,但是在我正在运行的单元测试中,我想等待一个特定的值为真。它先打印“等待”,然后打印“通知”(表示它应该正常工作),但永远不会打印“完成等待”。它并不总是这样,有时它工作得完美无缺,这就是为什么我认为这是某种比赛条件 我以前使用过锁定API,但也遇到了同样的问题,所以我决定尝试旧的wait/notify方法 在它有机会等待之前,它可能是正在创建和调用的侦听器吗?即使这是问题所在,也不应该先打印“等待”,然后再打印“通知”。即使我在“等待”和“通知”时打印ms,等待仍然在通知之前Java 奇怪的种族状况?,java,multithreading,race-condition,Java,Multithreading,Race Condition,我写的方法有问题。它所做的是等待一个属性等于另一个值。我不能在这里复制它,但是在我正在运行的单元测试中,我想等待一个特定的值为真。它先打印“等待”,然后打印“通知”(表示它应该正常工作),但永远不会打印“完成等待”。它并不总是这样,有时它工作得完美无缺,这就是为什么我认为这是某种比赛条件 我以前使用过锁定API,但也遇到了同样的问题,所以我决定尝试旧的wait/notify方法 在它有机会等待之前,它可能是正在创建和调用的侦听器吗?即使这是问题所在,也不应该先打印“等待”,然后再打印“通知”。即
private static <T> void waitFor(ObservableValue<T> value, T wantedValue) throws InterruptedException {
if (value.getValue() != wantedValue) {
Object lock = new Object();
synchronized (lock) {
value.addListener((observableValue, t, t1) -> {
if (value.getValue() != wantedValue) {
return;
}
synchronized (lock) {
System.out.print("notify");
lock.notifyAll();
}
});
System.out.println("wait");
lock.wait();
System.out.println("done waiting");
}
}
}
private static void waitFor(ObservalEvalue值,T wantedValue)抛出InterruptedException{
if(value.getValue()!=wantedValue){
对象锁=新对象();
已同步(锁定){
value.addListener((observeValue,t,t1)->{
if(value.getValue()!=wantedValue){
返回;
}
已同步(锁定){
系统输出打印(“通知”);
lock.notifyAll();
}
});
System.out.println(“等待”);
lock.wait();
System.out.println(“完成等待”);
}
}
}
没有道理
多个线程必须使用同一对象在上进行同步,否则同步将无法进行
最后,您的代码一直在等待一个信号,因为该对象是该方法的本地对象,所以其他线程都无法向lock
对象发送该信号
编辑:请忽略以上内容。这只是因为我忽略了锁对象被传递给匿名监听器,而匿名监听器是从方法中传递出来的
然而,另一个潜在的竞争条件正在发生
if (value.getValue() != wantedValue) {
Object lock = new Object();
synchronized (lock) { ...
如果值在执行getValue()
之后但在执行value.addListener(…)
之前异步更改,该怎么办
还有另一个小问题:forwait()
声明“可能出现虚假唤醒,此方法应始终在循环中使用”(强调我的方法)
没有道理
多个线程必须使用同一对象在上进行同步,否则同步将无法进行
最后,您的代码一直在等待一个信号,因为该对象是该方法的本地对象,所以其他线程都无法向lock
对象发送该信号
编辑:请忽略以上内容。这只是因为我忽略了锁对象被传递给匿名监听器,而匿名监听器是从方法中传递出来的
然而,另一个潜在的竞争条件正在发生
if (value.getValue() != wantedValue) {
Object lock = new Object();
synchronized (lock) { ...
如果值在执行getValue()
之后但在执行value.addListener(…)
之前异步更改,该怎么办
还有另一个小问题:for
wait()
声明“虚假唤醒是可能的,此方法应始终在循环中使用”(强调我的)。到目前为止关于锁的评论忽略了一个重要的细节。锁变量实际上不是局部变量。它在Java8中是“有效的最终”,因为它在侦听器闭包中被引用。这意味着它实际上被复制到实现closure对象的匿名类中的一个特殊编译器生成的字段中,因此,它实际上可能在调用waitFor()的线程和调用注册侦听器闭包的任何线程之间共享。所以,我认为这根本不是问题所在
然而,根据您期望发生的情况,代码中可能存在竞争条件。在调用wait()之前添加了侦听器,这意味着如果在您可以访问wait()之前有一个即时异步通知,侦听器对notifyAll()的调用将丢失,代码将永远等待已经发生的通知。当然,如果您希望将来有更多的通知,这并不是一个真正的问题,因为当下一个通知出现时,您将取消阻止,但是我希望您在单元测试中看到这个错误,在单元测试中,您只等待一个通知,有时您只是错过了它,这有时会导致测试失败
还有一个想法。也许您可以将代码更改为:
ObservableValue.waitFor(T值)
没有static关键字的代码几乎总是更好的(我希望它不存在)。如果ObservalEvalue是一个接口,那么您始终可以将waitFor()作为默认方法,因为您使用的是Java 8。到目前为止关于锁的评论忽略了一个重要的细节。锁变量实际上不是局部变量。它在Java8中是“有效的最终”,因为它在侦听器闭包中被引用。这意味着它实际上被复制到实现closure对象的匿名类中的一个特殊编译器生成的字段中,因此,它实际上可能在调用waitFor()的线程和调用注册侦听器闭包的任何线程之间共享。所以,我认为这根本不是问题所在 然而,根据您期望发生的情况,代码中可能存在竞争条件。在调用wait()之前添加了侦听器,这意味着如果在您可以访问wait()之前有一个即时异步通知,侦听器对notifyAll()的调用将丢失,代码将永远等待已经发生的通知。当然,如果您希望将来有更多的通知,这其实不是问题,因为您将在下一个通知出现时解除阻止,但是