多线程 - 为什么下面的程序表现得很奇怪?

Sud*_*dix 8 java multithreading

程序大纲:
我们有两个线程(t1t2)写一个整数值,然后将写入的值刷新到RAM.
另一个线程(t3)检查值巧合与所述一个是否写t1t2,如果没有,打印它.

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我认为会发生什么:
既然a不是volatile,我认为t3会缓存a,永远不会打印任何东西.

实际发生的事情:
大约一秒左右(无论你多久都t3睡不着),它会快速连续打印102或409.然后,打印停止(永远).

这到底发生了什么?

biz*_*lop 11

container.a不易挥发并不意味着它被强制缓存t3.这意味着无论是否存在都无法保证.

可以自由打印任何值的原因是此处的使用时间检查问题:

if (container.a != 409 && container.a != 102 )
   System.out.println(container.a);
Run Code Online (Sandbox Code Playgroud)

至于为什么在你测试它的确切环境中的这种确切行为,我们只能猜测.但我的理由是,虽然代码是按解释运行的container.a,但它每次都会进行读取,但是一旦Hotspot将其编译为本机代码,该值只会加载到寄存器中一次,这就是结束它.您可以使用-XX:+PrintCompilation命令行标志来验证此假设.

  • 最让我兴奋的是,如果打印了409或102,它可能会再次打印一个值,因为`a`是`int`并且写入它是原子的,所以它总是有409或102然后.答案当然是,有时候,当`container.a!= 409 && container.a!= 102`时,第一次读取得到102,第二次得到409. :-) (2认同)