mak*_*aks 12 java concurrency volatile
据我所知,在易失性读取之前发生了易失性写入 ,所以我们总是会在volatile变量中看到最新的数据.我的问题基本上涉及到之前发生的术语以及它发生在哪里?我写了一段代码来澄清我的问题.
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();
}
}
Run Code Online (Sandbox Code Playgroud)
(为清楚起见,省略了try catch块)
在这种情况下,我总是看到要在控制台上打印的值0.没有Thread.sleep(3000);我总是看到值10.这是发生在之前关系的情况还是打印'值10'因为线程1开始早一点线程2?
很高兴看到每个程序启动时带有和不带volatile变量的代码行为都不同的例子,因为上面代码的结果仅取决于(至少在我的情况下)线程顺序和线程休眠.
您会看到值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();
Run Code Online (Sandbox Code Playgroud)
之前发生的事情与写入在任何后续读取之前发生有关.如果写没有发生,那么真的没有关系.由于写线程处于休眠状态,因此在写入发生之前执行读取.
要观察行动中的关系,你可以有两个变量,一个是volatile,另一个是volatile.根据JMM,它表示在易失性写入发生之前,在易失性读取之前写入非易失性变量.
例如
volatile int a = 0;
int b = 0;
Run Code Online (Sandbox Code Playgroud)
线程1:
b = 10;
a = 1;
Run Code Online (Sandbox Code Playgroud)
线程2:
while(a != 1);
if(b != 10)
throw new IllegalStateException();
Run Code Online (Sandbox Code Playgroud)
Java内存模型表示b应始终等于10,因为非易失性存储发生在volatile存储之前.并且所有写入都发生在易失性存储发生之前的一个线程中 - 在所有后续的易失性加载之前.
| 归档时间: |
|
| 查看次数: |
2142 次 |
| 最近记录: |