Java线程可见性和同步

时间:2015-11-06 12:02:20

标签: java multithreading volatile

我一直在尝试Java线程可见性问题,通过共享布尔和非易失性变量向目标线程发送停止信号并且目标线程似乎没有得到它的流行示例如下所示:

public class ThreadVisibilityTest {

    //Shared variable to send a signal to the thread
    static boolean stopped = false;

    public static void main(String[] args) throws Exception {

        Thread targetThread = new Thread(new Runnable() {
                public void run() {
                while(!stopped) {}
                System.out.println("Target thread gets signal and stops...");
                }
                });

        targetThread.start();

        TimeUnit.SECONDS.sleep(5);
        stopped=true;
        System.out.println("Main thread has sent stop signal to the thread...");

    }

}

主线程通过将stopped设置为true,在5秒后向目标线程发送停止信号,目标线程无法获取,因此不会停止。

stopped变量定义为volatile显然可以解决问题。

然后我意识到如果我使stopped变量non volatile,而是在目标线程的synchronized上下文中访问它,目标线程将获取最终值并停止。因此,线程可见性问题似乎就像使用volatile一样解决了。

Thread targetThread = new Thread(new Runnable() {
            public void run() {
                while(true) {
                    synchronized(this) {
                        if(stopped) break; 
                    }
                }
                System.out.println("Target thread gets signal and stops...");
            }
        });

并且用于同步的对象监视器似乎没有如下效果:

synchronized(Thread.class) {
        if(stopped) break; 
}

这是偶然发生的事情还是我错过了什么? 或者我们可以说,通过互斥访问共享变量似乎迫使目标线程刷新其缓存,就像访问volatile变量一样?

如果后者是真的,你建议以哪种方式克服线程可见性问题,通过volatile关键字或互斥访问?

提前致谢

4 个答案:

答案 0 :(得分:2)

  

这是偶然发生的事情还是我错过了什么?

您错过了Java语言参考(JLS)中关于Java内存模型的章节。要么是这样,要么您错过了Java教程中的并发章节。 https://docs.oracle.com/javase/tutorial/essential/concurrency/

无论哪种方式,你都会知道如果线程A从同步块退出,然后线程B进入在同一个对象上同步的块,那么线程A在释放之前写入的所有内容在线程B锁定锁之后,保证锁对线程B可见。

答案 1 :(得分:0)

我认为互斥也提供了内存可见性,如Java Concurrency In Practice(Brian Goetz)第3.1.3节“锁定和可见性”中所述。

答案 2 :(得分:0)

请参阅,您在同步上下文中读取它,但不在同步上下文中写入。这可能会导致问题。

答案 3 :(得分:0)

正如其他答案已经指出的那样,使用同步时会建立内存可见性。

但是,最好使用易失性共享变量(除非与非可见性相关的问题需要同步),以实现更大的并发性。过度使用同步会迫使线程不断地等待对方,这时可以安全,更快地并发工作。