如何演示java多线程可见性问题?

Joe*_*e23 30 java multithreading visibility

如果从多个线程访问Java中的变量,则必须确保它们被安全地发布.这通常意味着使用synchronizedvolatile.

我的印象是,我的一些同事并没有认真对待这个问题,因为他们"以前从未听过,volatile而且他们的计划已经工作了多年".

所以我的问题是:

有人可以提供示例Java程序/片段,可靠地显示数据可见性问题.

我认为运行一个程序并看到意外的NPE或陈旧的变量值将有助于更多,而不仅仅是理论上的解释,这是无法证明的.

非常感谢你的帮助!

更新:再强调一点.我已经阅读了Java Concurreny in Practice,并且知道理论上存在可见性问题的示例.我正在寻找的是一种真正展示它们的方法.我不确定,这实际上是可行的,但也许有一个jvm配置或类似的东西允许它.

Dav*_*eas 21

通过修改例子在这里通过删除操作我想出了,始终无法在我的环境(线程永远不会停止运行)的例子.

// Java environment:
// java version "1.6.0_0"
// OpenJDK Runtime Environment (IcedTea6 1.6.1) (6b16-1.6.1-3ubuntu3)
// OpenJDK 64-Bit Server VM (build 14.0-b16, mixed mode)
public class Test2 extends Thread {
    boolean keepRunning = true;
    public static void main(String[] args) throws InterruptedException {
        Test2 t = new Test2();
        t.start();
        Thread.sleep(1000);
        t.keepRunning = false;
        System.out.println(System.currentTimeMillis() + ": keepRunning is false");
    }
    public void run() {
        while (keepRunning) 
        {}
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,此类问题完全取决于编译器/运行时/系统.特别是编译器可以确定添加指令以从内存中读取变量,即使它不是易失性的 - 因此代码可以工作 - ,vm和jit可以优化掉内存中的读取并仅使用寄存器,甚至是处理器可以重新排序指令 - 这不会影响这种情况,但在其他多线程情况下,如果修改了多个变量,它可能会影响其他线程的感知状态.

  • 大!非常感谢。该示例将在我的Windows笔记本电脑和带有client-vm的Solaris 10上运行,但在带有server-vm的Solaris和64位Linux上运行失败(即永久运行)。正是我想要的。最好的是,在Solaris上,在-client / -server之间切换会使其始终失败或始终运行。这应该使演示文稿中的重点。:) (2认同)

aio*_*obe 7

有人可以提供示例Java程序/片段,可靠地显示数据可见性问题.

不,没有可靠的示例显示数据可见性问题.

其原因是,在程序的任何有效的执行 volatile也是同样的程序的有效执行 volatile.(相反,显然不是真的!)


aio*_*obe 6

最常见的例子是强调使用volatile的重要性是一个while(keepRunning)例子:

public class Test extends Thread {

    boolean keepRunning = true;

    public static void main(String[] args) throws InterruptedException {
        Test t = new Test();
        t.start();
        Thread.sleep(1000);
        t.keepRunning = false;
        System.out.println(System.currentTimeMillis() + ": keepRunning is false");
    }

    public void run() {
        while (keepRunning) 
            System.out.println(System.currentTimeMillis() + ": " + keepRunning);
    }
}
Run Code Online (Sandbox Code Playgroud)

由于keepRunning可以(有效地)保存在运行while循环的线程的缓存中,因此该程序keepRunningkeepRunning设置为false 之后可以长时间打印"true" .

但请注意,没有可靠的方式来暴露竞争条件.(请参阅我的其他答案.)此示例可能会在某些硬件/操作系统/ jvm组合的某些情况下暴露它.