据我所知,易失性写入happens-before易失性读取,因此我们总是会看到易失性变量中最新鲜的数据。我的问题基本上是关于术语happens-before,它是在哪里发生的?我写了一段代码来澄清我的问题。
class Test {
volatile int a;
public static void main(String ... args) {
final Test t = new Test();
new Thread(new Runnable(){
@Override
public void run() {
Thread.sleep(3000);
t.a = 10;
}
}).start();
new Thread(new Runnable(){
@Override
public void run() {
System.out.println("Value " + t.a);
}
}).start();
}
}(为了清晰起见,省略了试捕捉块)
在这种情况下,我总是看到要在控制台上打印的值0。如果没有Thread.sleep(3000);,我总是看到值10。这是发生在关系之前还是打印‘值10’的情况,因为线程1启动的时间比线程2早一点?
在每个程序启动时,有和不带易失性变量的代码的行为会有所不同,这将是一个很好的例子,因为上面的代码的结果仅取决于线程的顺序和线程的休眠(至少在我的例子中是这样)。
发布于 2012-06-04 21:07:36
您可以看到值0,因为读是在写入之前执行的。您可以看到值10,因为写入是在读取之前执行的。
如果您想要具有更不可预测的输出的测试,则应该让两个线程等待一个CountDownLatch,以使它们同时启动:
final CountDownLatch latch = new CountDownLatch(1);
new Thread(new Runnable(){
@Override
public void run() {
try {
latch.await();
t.a = 10;
}
catch (InterruptedException e) {
// end the thread
}
}
}).start();
new Thread(new Runnable(){
@Override
public void run() {
try {
latch.await();
System.out.println("Value " + t.a);
}
catch (InterruptedException e) {
// end the thread
}
}
}).start();
Thread.sleep(321); // go
latch.countDown();发布于 2012-06-04 21:09:11
以前发生的事情实际上是在以后的任何读取之前发生的。如果写还没有发生,那就真的没有关系了。由于写入线程处于休眠状态,因此在写入之前执行读。
要观察行动中的关系,您可以有两个变量,一个是易失变量,另一个不是变量。根据JMM,它表示在易失性写入之前,对非易失性变量的写入要在易失性读取之前进行。
例如
volatile int a = 0;
int b = 0;线程1:
b = 10;
a = 1;线程2:
while(a != 1);
if(b != 10)
throw new IllegalStateException();Java模型指出,b应该始终等于10,因为非易失性存储发生在易失性存储之前。并且所有发生在一个线程中的所有写入都发生在一个易失性存储发生之前--在所有后续的易失性负载之前。
发布于 2012-06-04 21:16:50
不要停留在“发生之前”这个词上。它是jvm在R/W操作调度过程中使用的事件之间的关系。在这个阶段,它不会帮助你理解波动。重点是: jvm命令所有R/W操作。jvm可以命令任何它想要的(当然,服从所有同步,锁定,等待等)。现在:如果变量是不稳定的,那么任何读操作都会看到最新的写操作的结果。如果变量不是易失性的,那么它就不能保证(在不同的线程中)。就这样
https://stackoverflow.com/questions/10888293
复制相似问题