【问题标题】:The correct way to test "Volatile" in java在java中测试“Volatile”的正确方法
【发布时间】:2021-11-11 01:17:45
【问题描述】:

我已经阅读了java中volatile的定义,我运行了一个如下的小测试项目:

public class MainActivity extends AppCompatActivity
{

    static int num = 0;
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        Thread readerThread = new Thread(() -> {
            int temp = 0;
            while (true)
            {
                if (temp != num) {
                    temp = num;
                    System.out.println("reader: value of num = " + num);
                }
            }
        });

        Thread writerThread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                num++;
                System.out.println("writer: changed value to = " + num);

                //sleep for readerThread to have enough time to read the change of num, since num++ is not atomic
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.exit(0);
        });

        readerThread.start();
        writerThread.start();

    }
}

如果我在 Eclipse 上运行主代码,我会得到如下日志:

writer: changed value to = 1
reader: value of num = 1
writer: changed value to = 2
writer: changed value to = 3
writer: changed value to = 4
writer: changed value to = 5

也就是说,我猜是对的,结果意味着读取器没有从主存中获取值,而是从缓存中获取。

但是,如果我通过 android studio 在真实的 android 设备上构建代码,我得到的日志是:

I/System.out: reader: value of num = 1
I/System.out: writer: changed value to = 1
I/System.out: reader: value of num = 2
I/System.out: writer: changed value to = 2
I/System.out: writer: changed value to = 3
I/System.out: reader: value of num = 3
I/System.out: writer: changed value to = 4
I/System.out: reader: value of num = 4
I/System.out: writer: changed value to = 5
I/System.out: reader: value of num = 5

似乎没有 volatile 读者仍然可以从主存中获取值,为什么这两者之间的结果不同?

【问题讨论】:

  • 所以...如果您使用 volatile,则未指定读者是否会看到作者所做的更改。而且您在不同的平台上运行代码,在这种 unspecified 案例中发生的事情是不同的。当您的应用程序执行超出 Java 指定行为的事情时,就会发生这种情况。
  • 而不是找出“测试”,而是找出规范并确保您的代码能够正常工作。这里有很多的问题,特别是最大的一个是num++,你认为它是原子的,而原子性与volatile无关
  • 我的意思是 volatile 是关于 happens-before 边缘,而不是“主内存”。 volatile 保证在规范中。其他一切都很好,未指定(船长很明显,我知道)。当某些事情未指定时 - 您处于危险之中,无论是现在,十年后,在 Android 上还是在 M1 上。
  • 顺便说一句,您的问题以“我已经阅读了 java 中 volatile 的定义...”开始,但后来您使用“主内存”之类的词并使用 num++ 等。我想你最初的灵感来源并不好。有很多关于这个的好文章,但我会给你this link(我的,是的)应该让你开始
  • 是的。在存在“数据竞赛”的地方,可能会发生很多丑陋的事情。

标签: java android multithreading eclipse volatile


【解决方案1】:

首先我认为您需要使用JCStress 框架来测试与Java 并发相关的所有内容。这将为您提供可靠的结果。

其次,当您写“结果意味着读取器没有从主存中获取值,而是从缓存中获取值”时,您就踩到了非常薄的冰块,因为 JLS 没有在主存方面指定任何特定行为或缓存,两者都是平台相关的实现细节。 JLS 只是指定了运行时必须遵循的某些规则。您可能有一个在 CPU 中根本没有缓存的想象平台,因此深入到语言级别是没有意义的。

第三,我想当您在 Eclipse 上运行代码时,引擎盖下有一些 x86,如果是 Android,它可能是 ARM,所以如果没有适当的同步,很难期望得到相同的结果。

【讨论】:

  • 即使忽略 JMM,考虑刷新到主内存也是有缺陷的。现代处理器上的缓存始终是一致的,强制写入主内存会使并发应用程序非常缓慢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-13
  • 2017-06-06
  • 1970-01-01
相关资源
最近更新 更多