7

我最近在 SO 和其他地方阅读了很多关于线程内存管理的内容,特别是volatile关键字的使用。我开始对这个概念有相当的信心,但是,为了充分了解它的效果,我想尝试运行一些实验来说明它。

这是我的设置:我有一个生产者线程(它从麦克风读取音频数据,与我之前的问题相关,但实际数据无关紧要)将数据传递byte[]给单独的消费者线程。线程之间共享数据的方式是我实验中的主要变量:我尝试过ArrayBlockingQueue; 我尝试了共享volatile byte[]参考(使用此博客文章array = array中推荐的自我参考);而且我还尝试了没有自引用的普通非易失性。两个线程同时也将数据写入磁盘。byte[]

我的希望是发现,非易失byte[]版本在运行一段时间后,由于一些内存写入不及时可见,生产者尝试共享的数据与消费者读取数据的数据之间存在差异,而其他两个版本的每个线程记录的数据完全相同,因为采取了确保发布内存写入的预防措施。然而,碰巧的是,无论我使用什么方法,我都发现 100% 准确。

关于为什么会发生这种情况,我已经想到了一些可能性,但我的主要问题是: 在什么条件下写入另一个线程看不到的非易失性变量,据我所知,这就是重点volatile?我可以强制这些条件用于实验目的吗?

到目前为止,我的想法是:

  • 也许这两个线程在同一个核心上运行并共享同一个缓存,所以内存写入是立即可见的?
  • 也许CPU负载是一个因素?在我看到任何问题之前,也许我需要很多线程都在做不同的事情?
  • 也许我需要等待更长的时间:也许这样的问题非常罕见?

谁能建议我如何设计这样的实验或解释我的想法有缺陷的原因?

非常感谢。

4

1 回答 1

7

您将无法在 x86 上轻松观察代码中缺少障碍的影响,因为它具有相当强大的内存模型。但这并不意味着相同的代码不会在不同的架构上中断。在 x86 上,您通常需要使用 JIT 编译器并帮助它进行 volatile 变量不允许的优化,例如变量提升。

下面的代码在我的带有热点 7u25 服务器的机器上,如果变量是非易失性的,则永远不会结束,但如果是,则会立即停止。您可能需要根据您的机器更改睡眠延迟。

public class Test {

    static /* volatile */ boolean done = false;

    public static void main(String[] args) throws Exception {
        Runnable waiter = new Runnable() {
            @Override public void run() {
                while(!done);
                System.out.println("Exited loop");
            }
        };
        new Thread(waiter).start();
        Thread.sleep(100); //wait for JIT compilation
        done = true;
        System.out.println("done is true");
    }
}
于 2013-08-29T17:28:48.187 回答