对于并发而言我是新手,在发现问题时我不确定自己,我正在寻找一个相当成熟的代码库并找到以下代码(为简洁而编辑),我认为这些代码容易受到数据竞争的影响:< / p>
public class Example extends Thread {
boolean condition = false;
public void run () {
while (true) {
synchronized (this) {
try {
while( condition ) wait();
}
catch (InterruptedException e) { /*for brevity*/ }
}
// non-blocking computation
}
}
public void setTrue () { condition = true; }
public void setFalse () {
synchronized (this) {
condition = false;
this.notifyAll();
}
}
}
据我所知condition
必须是volatile,因为即使使用synchronized块,编译器也不会发出任何内存障碍;如果它是condition
中setTrue
的易失性存储,则编译器会发出StoreEnter.
我是否有理由相信上述内容容易受到数据竞争的影响?如果是这样,我怎么能通过一个例子见证数据竞争(而不是简单地了解JMM提供的保证)。使用线程在循环中随机调用setTrue
的简单测试不会发现数据竞争。
另外,我认为使用notifyAll在这里有点过分,因为有一个条件要检查,只有一个线程会等待它,对吧?
谢谢。
答案 0 :(得分:3)
据我所知,条件必须是易变的,因为即使使用synchronized块,编译器也不会发出任何内存障碍;如果它是在setTrue中调节的易失性存储,编译器将发出StoreEnter。
这是不正确的。当您在synchronized
块中使用共享变量时,对于使用具有相同锁的相同变量的其他线程,您的代码将是线程安全的。如果需要内存屏障,则会使用它们。
但是,您向我们展示的代码不正确,因为setTrue()
方法正在更新synchronized
块之外的标记。
我是否有理由相信上述内容容易受到数据竞争的影响?
是的......有点儿。方案如下:
false
。setTrue
,它将条件变量设置为缓存中的true
。但由于setTrue
方法不使用synchronized
,因此没有写入障碍,也没有刷新到主内存。 false
),并且不会按原样等待。另外,我认为使用notifyAll在这里有点过分,因为有一个条件要检查,只有一个线程会等待它,对吗?
如果这是你的意思,可以用notify()
代替......但说实话,通知你使用的风格并没有什么不同。
您评论道:
我的意思是编译器认为在这种情况下不需要提交内存屏障。
也许。但是“monitorenter”和“monitorexit”指令隐含地涉及记忆障碍。
和
如果条件不稳定会不会也是正确的?
如果您正在讨论使用volatile
AND synchronized
,那么肯定是正确的......虽然volatile
会多余(假设setTrue
错误是固定的。)
如果您只谈论volatile
,那么不。您无法仅使用volatile
实现有效的“等待条件变量”。问题是“读取/测试/等待”或“写入/通知”序列都不能以原子方式执行;即没有竞赛条件的可能性。
此外,如果不使用原始对象互斥锁或wait/notify
对象,则无法执行等效的Lock
。
答案 1 :(得分:0)
我是否有理由相信上述内容容易受到数据竞争的影响?
不要这么认为。条件不重要,它只允许方法避免等待。它的设置方式也不重要。它不需要是volatile,因为它的使用是一个对象的本地。
另外,我认为使用notifyAll在这里是有点过头了 一个要检查的条件,只有一个线程会等待它, 正确?
NotifyAll没问题,虽然只有一个线程等待 方法,但可能还有很多其他线程在等待或等待线程。