不确定volatile的这种用法是否有意义,似乎也有同样的问题

时间:2011-12-08 14:45:40

标签: java concurrency

在这里阅读:

JLS 8.3.1.4 volatile Fields

没有volatile就说

  

“然后方法二偶尔可以打印j的值大于i的值,因为该示例不包含同步和”

class Test {
    static volatile int i = 0, j = 0;
    static void one() { i++; j++; }
    static void two() {
        System.out.println("i=" + i + " j=" + j);
    }
}

用volatile表示

  

“但是,对于方法二的任何给定调用都可能会观察到j的值远远大于i观察到的值,因为方法一可能会执行多次在方法二取值i的时刻与方法二取值j的时刻之间。“

在行为'正确'与同步化,但我很困惑volatile带来什么好处?

我认为volatile gaurantees保留了顺序,所以我会想到在某些情况下,i的值可能大于j,但不是相反,因为这意味着顺序增量改变了。

这是文档中的拼写错误吗?如果没有,请在使用j时说明i如何大于volatile

6 个答案:

答案 0 :(得分:2)

据说在方法二中间方法可以运行几个,并且为j读取的值将高于为i读取的值。

read i
run method 1
run method 1
read j

答案 1 :(得分:1)

volatile变量告诉JIT编译器不要执行任何可能影响对该变量的访问顺序的优化。对volatile变量的写入总是在内存中执行,而不是在高速缓存或cpu寄存器上执行。

还有两点:

  • i ++不是单个操作,而是三个:a)读取变量i,b)增量,c)存储。这种“三重”操作不是原子操作,这意味着如果没有其他线程查看其不一致状态,则无法保证完成。如果您想这样做,请查看AtomicInteger#getAndIncrement()
  • 您的方法one()未同步,因此您可以让一个线程完成i ++,然后打印第二个线程,然后第一个线程完成j ++操作。

答案 2 :(得分:1)

根据我的理解,volatile保证不同的线程将引用相同的变量而不是复制它,即,如果更新线程中的volatile变量,所有其他线程将更新此变量,因为它们都引用相同的变量。可以在Why Volatile Matters找到一个很好的例子。

关于方法二的事情是它不是Atomic。它不会仅在一个CPU周期中运行。您可以在@Sign声明的不同操作中进行划分。即使i ++不是原子的,因为它需要读取变量i,增加它并将其再次存储在内存中的i引用中。

答案 3 :(得分:1)

你有挥发性的权利;你只是没有仔细阅读你引用的内容:

  

因为方法一可能会在一瞬间执行多次   当方法二取出i的值和方法二的时刻   获取j的值

订单保留在one()中,即two()中,i被提取并打印,但在打印ii j通过从其他线程调用one(),可能会多次递增j,因此i的打印值将高于{{1}}的打印值。< / p>

答案 4 :(得分:0)

volatile使读取或写入线程安全/内存一致。然而,它不会使读取和写入原子。如果只有一个线程会更新它,那么使用volatile就可以了。

我建议您使用AtomicInteger。

答案 5 :(得分:0)

在“源代码顺序”中,增加到i 发生在增量为j之前。因此,线程调用方法one()将始终遵守i >= j。但是,当其他线程观察到这些变量时,他们可以看到不同的东西。

某些事件确定了JLS称之为“同步顺序”的内容。将这些事件(以及这些事件)称为“在其他人之前发生”是有意义的。写入易失性变量就是其中之一。在不使用volatile的情况下,在 i之前说j递增没有任何意义;这些写入可以重新排序,其他线程可以观察到重新排序。

没有volatile可能发生的事情的一个更好的例子是:

static void oneAndAHalf() { System.out.println("j=" + j + " i=" + i);

即使j 出现 i递增 之前 > j,您仍然可以观察i,因为删除j > i会允许对volatile中的操作进行重新排序。添加one()volatile将始终显示oneAndAHalf(),正如您所期望的那样。

如果您带走i >= j,则方法volatile可以打印two()的值大于j,原因有两个:因为操作已重新排序,因为ii未被原子化处理。当前的j方法并未明确说明two()的效用。添加volatile,您将获得相同的输出,但唯一的原因是操作不是原子操作。

要查看一致的视图,volatile,两种方法都可以i == j。这将使两个变量的增量看起来都是原子的。