在Java规范中无法理解volatile的例子

前端之家收集整理的这篇文章主要介绍了在Java规范中无法理解volatile的例子前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我得到了一般的理解,在 Java中有什么挥之不去但阅读
Java SE Specification 8.3.1.4我有一个问题,了解一些挥发性的例子下面的文字.
class Test {
    static volatile int i = 0,j = 0;
    static void one() { i++; j++; }
    static void two() {
        System.out.println("i=" + i + " j=" + j);
    }
}

This allows method one and method two to be executed concurrently,but
guarantees that accesses to the shared values for i and j occur
exactly as many times,and in exactly the same order,as they appear
to occur during execution of the program text by each thread.
Therefore,the shared value for j is never greater than that for i,
because each update to i must be reflected in the shared value for i
before the update to j occurs. It is possible,however,that any given
invocation of method two might observe a value for j that is much
greater than the value observed for i,because method one might be
executed many times between the moment when method two fetches the
value of i and the moment when method two fetches the value of j.

怎么

j never greater than i

,但同时

any given invocation of method two might observe a value for j that is
much greater than the value observed for i

??

看起来像矛盾.

运行示例程序后,我的j比我大.为什么使用挥发性呢?它给出几乎相同的结果没有挥发性(我也可以大于j,前面的规范之一).为什么这个例子在这里作为同步的替代方法

解决方法

我认为这个例子是要强调,使用volatile时需要注意和确保顺序;该行为可能是反直觉的,该示例演示了它.

我同意这个措词有点模糊,有可能为多个案例提供更明确和明确的例子,但没有矛盾.

共享值是同一时刻的值.如果两个线程在完全相同的时刻读取i和j的值,则j的值将不会被观察到大于i.易于保证在代码中保持读取和更新顺序.

然而,在样本中,打印i和j是两个不同的操作,间隔任意时间;因此,可以观察到j大于i,因为可以在读取i之后和读取j之前任意次数更新.

使用volatile的一点是,当您以正确的顺序同时更新和访问volatile变量时,您可以假设原则上不可能发生波动.

在上面的示例中,two()中的访问顺序不允许以哪个变量大于或等于的置信度得出结论.

但是,如果将样本更改为System.out.println(“j =”j“i =”i);

在这里,您可以断定j的打印值不会大于i的打印值.由于两个原因,这种假设不会有波动.

首先,更新i和j可以由编译器和硬件以任意顺序执行,实际上可以以j执行; i.如果来自其他线程,那么您可以在j之后访问j和i,但在我之前,您可以观察到j = 1和i = 0,无论访问顺序如何. volatile保证不会发生这种情况,它将按照您的源代码中的顺序执行操作.

其次,volatile保证另一个线程会看到最近的值被另一个线程改变,只要它在最后一次更新后的较晚时间内访问它.没有波动,对于观测值不能有假设.在理论上,值可以永远保持在另一个线程零点.该程序可以从过去的更新中打印两个零,零和任意数字等;其他线程中的观察值可能会小于更新器线程在更新后看到的当前值.在第一个更新之后,volatile将保证在第二个线程中看到值.

虽然第二个保证似乎是第一个(订单保证)的结果,但实际上它们是正交的.

关于同步,它允许执行非原子操作的序列,例如i; j作为原子操作,例如如果一个线程同步i; j,另一个线程同步System.out.println(“i =”i“j =”j);第一个线程可能不执行增量序列,而第二个打印,结果将是正确的.

但这是一个代价.首先,synhronized本身具有性能损失.第二,更重要的是,并不总是需要这样的行为,并且阻塞的线程浪费时间,降低了系统吞吐量(例如,您可以在System.out中执行许多i; j;).

猜你在找的Java相关文章