【问题标题】:volatile jls example易失性 jls 示例
【发布时间】:2013-01-13 19:29:06
【问题描述】:

下面的代码是否应该不会根据JLS example for volatile 在 windows 7 x86 jdk 7(打开 -ea)上抛出 AssertionError?

public class TestVolatile {
static volatile int i = 0;
static volatile int j = 0;

static void one() {
    i++;
    j++;
    assert (i>=j);
//:"one: i=" + i + " j=" + j;
}
static void two() {
    //System.out.println("i=" + i + " j=" + j);
    assert (i<=j);
    /*
    System.out.print("<i=" + i);
    for (int k = 0; k < 1000000; k++);
    System.out.println(", j=" + j+">");
    */
}
public static final int NUM_WORKERS =  4;

public static void main (String [] args) {
    final Worker [] workers = new Worker[NUM_WORKERS];
    final Thread [] workerThreads = new Thread[NUM_WORKERS];

    for (int i = 0; i < NUM_WORKERS; i++) {
        Worker w = new Worker(i);
        workers[i] = w;
        workerThreads[i] = new Thread(w,"workerThread_"+i);
    }

    for (int i = 0; i < NUM_WORKERS; i++) {
        workerThreads[i].start();
    }

}


}

final class Worker implements Runnable {
final int id;
volatile boolean notDone = true;


public Worker(int tid){
    id = tid;
}

@Override
public void run() {
    //System.out.println("worker start:" + id);
    try {
        while (notDone) {
            if (id  <  TestVolatile.NUM_WORKERS - 1) {
                TestVolatile.one();
            } else {
                TestVolatile.two();
            }
        }
    } catch (Exception e) {
        // TODO: handle exception
        e.printStackTrace();
    }
}
};

【问题讨论】:

  • 我几乎马上就收到了错误。
  • 您预计会在哪里出错,为什么?澄清
  • 我也得到了错误,没有任何延迟,而且确实如此。
  • @MarkoTopolnik 老实说,我发现example 8.3.1.4-1 底部的措辞有点误导:因此,j 的共享值永远不会大于 i,因为每次更新到在更新到 j 之前,i 必须反映在 i 的共享值中。
  • @assylias 即使对于单个作者来说,这个陈述也是错误的,甚至下一句也承认:“然而,任何给定的方法二调用都可能观察到j 远大于对 i 观察到的值,...”。所以 j 可以大于 i 并且在前面的句子中说否则是......充其量是误导。有a new Q&A 关于这个和恕我直言,这个例子应该在地狱中燃烧......

标签: java multithreading concurrency volatile jls


【解决方案1】:

您有超过 1 个线程在运行 oneij 是易变的,因此更改将是可见的,但是 i++j++ 不是原子操作,并且很可能其中一个计数器在某个阶段不会正确递增:

例如,假设 i 为 5,以显示可能引发 AssertionError 的有效线程交错:

  • 线程 1:读取 i => 5
  • 线程 2:读取 i => 5
  • 线程 1:temp = i + 1 => 6
  • 线程 2:temp = i + 1 => 6
  • 线程 1:写 i = temp => 6
  • 线程 2:写 i = temp => 6
  • 线程 1:读取 j 并递增 => j = 6
  • 线程 2:读取 j 并递增 => j = 7

并且 i 和 j 不同步,您在 one 中的断言将失败。

但是,尽管存在数据竞争,但程序的运行仍然可能不会引发断言错误:JLS 不保证您的程序将运行良好,但它并没有说它会运行'也可以。

编辑:编写 JLS 的“线程和锁”部分的人之一实际上有一个 post on his blog 关于一个非常相似的问题。 cmets 甚至提到了您在问题中提到的 JLS 部分:JLS 示例中只有一个编写线程。

【讨论】:

  • +1 volatile 并不意味着对它的所有操作都是原子的,只能读取或写入,但不能同时进行。我相信给出的示例仅适用于这是一个执行更新的线程。
  • 我不确定该示例是否会通过,即使只有一个线程执行更新。但我完全同意这是测试代码中的一个原子问题。谢谢大家,这里就像是 JLS 的实时演习。
  • 另一个证明 volatile 不能确保原子性的例子:brooker.co.za/blog/2012/11/13/increment.html
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-18
  • 1970-01-01
  • 2016-01-18
  • 2023-03-13
相关资源
最近更新 更多