2015-11-06 45 views
1

我一直在试验Java线程可见性问题,通过一个共享的布尔和非易失性变量向目标线程发送一个停止信号到线程的流行示例,目标线程似乎没有得到它)如下图所示: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而是访问它在目标线程​​背景,目标线程得到最终值和停止。所以线程可见性问题似乎可以像使用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一样吗?

如果后者是真实的你建议克服线程可见性问题,通过挥发性关键字或访问互斥?

在此先感谢

+0

如果您知道必须使用此变量的最新值,为什么您会选择不使用易失性?你认为它有什么问题? –

+0

如果你想使用同步,你当然必须做在相同的监视器上同步停止块的写入和读取。您不会在同步块中写入该值。 –

+0

我不是不想选择易变的。我试图理解为什么互斥也解决了内存可见性问题,因为我已经测试并发现,通过互斥来访问共享变量会使您像volatile一样获得最终值。 – Serdar

回答

0

我觉得互斥还提供了部分3.1.3锁定和能见度在Java并发在实践声明(Brian Goetz撰写)内存的知名度。

2

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

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

无论哪种方式,你会了解到,如果从一个synchronized块线程A退出,然后线程B进入是同一对象上同步块,那么线程A一切释放锁是以前写保证在线程B锁定锁之后对线程B可见。

0

看到,您正在同步的上下文中阅读它,但未在同步上下文中编写。这可能会导致问题。