程序概述:
我们有两个线程(t1
和t2
)写入一个整数值,然后将写入的值刷新到RAM。
另一个线程(t3
)检查该值是否与t1
或t2
所写的值相符,如果不一致,则打印该值。
public class Container
{
int a;
volatile boolean b;
public static void main(String[] args)
{
Container container = new Container();
Thread t1 = new Thread()
{
@Override
public void run()
{
for (;;)
{
container.a = 409;
container.b ^= container.b;
}
}
};
Thread t2 = new Thread()
{
@Override
public void run()
{
for (;;)
{
container.a = 102;
container.b ^= container.b;
}
}
};
Thread t3 = new Thread()
{
@Override
public void run()
{
try
{
Thread.sleep(100);
} catch (InterruptedException e)
{
e.printStackTrace();
}
for (;;)
{
if (container.a != 409 && container.a != 102 )
System.out.println(container.a);
}
}
};
t1.start();
t2.start();
t3.start();
}
}
我认为会发生的事情:
由于a
不是volatile
,因此我认为t3
会缓存a
而从不打印任何内容。
实际发生的情况:
一秒钟左右(无论您使t3
处于休眠状态),它都会快速连续打印102或409。然后,打印停止(永远)。
这里到底发生了什么?
答案 0 :(得分:11)
container.a
不易失并不意味着它被t3
强制缓存。这意味着没有任何保证。
可以自由打印任何值的原因是这里的使用时间检查问题:
if (container.a != 409 && container.a != 102 )
System.out.println(container.a);
至于为什么在您测试它的确切环境中出现这种确切行为,我们只能猜测。但是我的钱是基于这样的理论,即代码以解释的方式运行时,每次都会去读取container.a
,但是一旦Hotspot将其编译为本地代码,则该值只会被加载到寄存器一次,到此为止。您可以使用-XX:+PrintCompilation
命令行标志来验证这一假设。