多线程代码中的非易失性状态标志

时间:2013-08-18 02:20:20

标签: java concurrency volatile java.util.concurrent

对于并发而言我是新手,在发现问题时我不确定自己,我正在寻找一个相当成熟的代码库并找到以下代码(为简洁而编辑),我认为这些代码容易受到数据竞争的影响:< / 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块,编译器也不会发出任何内存障碍;如果它是conditionsetTrue的易失性存储,则编译器会发出StoreEnter.

我是否有理由相信上述内容容易受到数据竞争的影响?如果是这样,我怎么能通过一个例子见证数据竞争(而不是简单地了解JMM提供的保证)。使用线程在循环中随机调用setTrue的简单测试不会发现数据竞争。

另外,我认为使用notifyAll在这里有点过分,因为有一个条件要检查,只有一个线程会等待它,对吧?

谢谢。

2 个答案:

答案 0 :(得分:3)

  

据我所知,条件必须是易变的,因为即使使用synchronized块,编译器也不会发出任何内存障碍;如果它是在setTrue中调节的易失性存储,编译器将发出StoreEnter。

这是不正确的。当您在synchronized块中使用共享变量时,对于使用具有相同锁的相同变量的其他线程,您的代码将是线程安全的。如果需要内存屏障,则会使用它们。

但是,您向我们展示的代码不正确,因为setTrue()方法正在更新synchronized块之外的标记。


  

我是否有理由相信上述内容容易受到数据竞争的影响?

是的......有点儿。方案如下:

  1. 条件为false
  2. 其他一些线程调用setTrue,它将条件变量设置为缓存中的true。但由于setTrue方法不使用synchronized,因此没有写入障碍,也没有刷新到主内存。
  3. “example”线程从主内存中获取最新的提交值(仍为false),并且不会按原样等待。
  4.   

    另外,我认为使用notifyAll在这里有点过分,因为有一个条件要检查,只有一个线程会等待它,对吗?

    如果这是你的意思,可以用notify()代替......但说实话,通知你使用的风格并没有什么不同。


    您评论道:

      

    我的意思是编译器认为在这种情况下不需要提交内存屏障。

    也许。但是“monitorenter”和“monitorexit”指令隐含地涉及记忆障碍。

      

    如果条件不稳定会不会也是正确的?

    如果您正在讨论使用volatile AND synchronized,那么肯定是正确的......虽然volatile会多余(假设setTrue错误是固定的。)

    如果您只谈论volatile,那么不。您无法仅使用volatile实现有效的“等待条件变量”。问题是“读取/测试/等待”或“写入/通知”序列都不能以原子方式执行;即没有竞赛条件的可能性。

    此外,如果不使用原始对象互斥锁或wait/notify对象,则无法执行等效的Lock

答案 1 :(得分:0)

  

我是否有理由相信上述内容容易受到数据竞争的影响?

不要这么认为。条件不重要,它只允许方法避免等待。它的设置方式也不重要。它不需要是volatile,因为它的使用是一个对象的本地。

  

另外,我认为使用notifyAll在这里是有点过头了   一个要检查的条件,只有一个线程会等待它,   正确?

NotifyAll没问题,虽然只有一个线程等待 方法,但可能还有很多其他线程在等待或等待线程。