Can';不理解Java规范中的volatile示例
我大致了解了Can';不理解Java规范中的volatile示例,java,volatile,specifications,Java,Volatile,Specifications,我大致了解了volatile在Java中的含义。但是阅读 我在理解那个易变的例子下面的文字时遇到了问题 class Test { static volatile int i = 0, j = 0; static void one() { i++; j++; } static void two() { System.out.println("i=" + i + " j=" + j); } } 这允许方法1和方法2同时执行,但是 保证访问i和j的共享
volatile
在Java中的含义。但是阅读
我在理解那个易变的例子下面的文字时遇到了问题
class Test {
static volatile int i = 0, j = 0;
static void one() { i++; j++; }
static void two() {
System.out.println("i=" + i + " j=" + j);
}
}
这允许方法1和方法2同时执行,但是
保证访问i和j的共享值
与它们出现的次数和顺序完全相同
在每个线程执行程序文本期间发生。
因此,j的共享值永远不会大于i的共享值,
因为对i的每次更新都必须反映在i的共享值中
在更新到j之前。然而,任何给定的
调用方法2可能会观察到j的一个值,这个值非常大
大于为i观察到的值,因为方法1可能是
在方法2获取
i的值和方法2获取j值的时刻
怎么样
j永远不会比我伟大
,但同时
方法2的任何给定调用都可能观察到一个j值,即
远大于观察到的i值
?
看起来很矛盾
运行示例程序后,我得到的
j
大于I
。那么为什么要使用volatile呢?它给出了几乎相同的结果,而没有volatile
(而且i
可以大于j
,这是规范中以前的示例之一)。为什么这里的示例是作为同步的替代方案?所有这些都取决于您如何使用它。Java中的volatile关键字用作Java编译器和线程的指示符,Java编译器和线程不缓存该变量的值,并且总是从主内存中读取它。因此,如果您想通过实现共享任何读写操作是原子的变量,例如,在int或布尔变量中读写,那么您可以将它们声明为volatile变量
从Java 5开始,再加上诸如自动装箱、枚举、泛型和变量参数等主要更改,Java在Java内存模型(JMM)中引入了一些更改,这保证了从一个线程到另一个线程所做更改的可视性,就像“之前发生”一样,它解决了在一个线程中发生的内存写入可能“泄漏”的问题然后被另一条线看到
Java volatile关键字不能与方法或类一起使用,只能与变量一起使用。Java volatile关键字还保证了可见性和顺序,在Java 5写入任何volatile变量之后,在读取任何volatile变量之前。顺便说一句,volatile关键字的使用还可以防止编译器或JVM对代码进行重新排序或使它们远离同步障碍
Java中Volatile关键字的要点
Java中的volatile关键字是变量的唯一应用程序,在类和方法中使用volatile关键字是非法的
Java中的volatile关键字保证volatile变量的值总是从主内存中读取,而不是从线程的本地缓存中读取
在Java中,对于使用Java volatile关键字声明的所有变量(包括长变量和双变量),读写都是原子的
在Java中对变量使用volatile关键字可以降低内存一致性错误的风险,因为在Java中对volatile变量的任何写入都会与该变量的后续读取建立“发生在之前”的关系
在任何时候,则j
不大于i
这与方法2观察到的不同,因为它在不同的时间访问变量i
和j
<首先访问代码>i
,然后稍晚访问j
这不是同步版本的直接替代方案,因为行为不同。不使用volatile的一个区别是,如果不使用volatile,可以始终打印0的值。增量永远不需要是可见的
该示例演示了易失性访问的顺序。需要这样做的示例可能是:
volatile boolean flag = false;
volatile int value;
// Thread 1
if(!flag) {
value = ...;
flag = true;
}
// Thread 2
if(flag) {
System.out.println(value);
flag = false;
}
线程2读取线程1设置的值,而不是旧值
为什么j永远不会比我大
假设您只执行一次one()
。在这个方法的执行过程中,i总是在j之前递增,因为递增操作一个接一个地发生。
如果同时执行one()
,则每个单独的方法调用将等待执行队列中的其他方法完成将其值写入i或j,具体取决于当前执行的方法尝试递增的变量。因此,对i的所有写入都是一个接一个地发生的,对j的所有写入都是一个接一个地发生的。因为在方法体中,i在j之前递增,在给定的时刻,j永远不会大于i
方法2的任何给定调用都可能观察到j的值远大于i的值,如何
如果调用two()
时在后台执行方法one()
,则在读取i
和读取j
之间,可以多次执行方法一。因此,当读取i
的值时,它可能是在时间t=0时调用one()
的结果,而当读取j
的值时,它可能是稍后调用one()
的结果,例如在时间t=10时。因此,在本例中,println
语句中,j
可以大于i
为什么使用volatile代替synchronized
我不会列出任何人应该使用volatile
而不是synchronized
块的所有原因。但是请记住,volatile
保证了原子访问
void f()
{
int x=0, y=0;
x++;
print( x>y );
y++
}
static void two() {
System.out.println("j=" + j + " i=" + i);
}