线程之间的可见性

服务器模式运行下面的Java程序: ( 默认为client模式) (本机使用的是Oracle的Hotspot VM)

命令行代码   收藏代码
  1. java -server StopThread  

 

Java代码   收藏代码
  1. import java.util.concurrent.TimeUnit;  
  2.   
  3. // Broken! - How long would you expect this program to run ?  
  4. public class StopThread {  
  5.   
  6.     private static boolean stopRequested;  // value: false  
  7.       
  8.     public static void main(String... args) throws InterruptedException {  
  9.           
  10.         Thread backgroundThread = new Thread(new Runnable() {  
  11.               
  12.             @Override  
  13.             public void run() {  
  14.                 int i = 0;  
  15.                 while(!stopRequested)  
  16.                     i++;  
  17.             }  
  18.         });  
  19.           
  20.         backgroundThread.start();  
  21.           
  22.         TimeUnit.SECONDS.sleep(1);  
  23.         stopRequested = true;  
  24.     }  
  25. }  

 这个例子有两个线程,一个是main线程,另一个是在main线程里启动的backgroundThread线程,这两个线程共享一个类型为boolean的stopRequested。理论上如果main线程将stopRequested变量设置为true时,backgroundThread线程会跳出循环然后结束,接着JVM也终止。但是在我的机子上(Intel i5 M460),它一直处于运行中,并不会在1秒中后停止运行。

原因要归咎于Java语言规范(JLS)中的内存模型(Memory Model)。它规定了一个线程所做的变化何时以及如何变成对其它线程可见(可见性)。因此我们不知道何时后台线程能够“看到”main线程对stopRequested的值所做的改变。一个简洁,性能好的方案是对共享变量添加volatile关键字。volatile保证任何一个线程在读取该域的时候都能“看到”最近刚刚被写入的值。

修改后的代码:

Java代码   收藏代码
  1. import java.util.concurrent.TimeUnit;  
  2.   
  3. // Broken! - How long would you expect this program to run ?  
  4. public class StopThread {  
  5.   
  6.     private static boolean volatile stopRequested;  // value: false  
  7.       
  8.     public static void main(String... args) throws InterruptedException {  
  9.           
  10.         Thread backgroundThread = new Thread(new Runnable() {  
  11.               
  12.             @Override  
  13.             public void run() {  
  14.                 int i = 0;  
  15.                 while(!stopRequested)  
  16.                     i++;  
  17.             }  
  18.         });  
  19.           
  20.         backgroundThread.start();  
  21.           
  22.         TimeUnit.SECONDS.sleep(1);  
  23.         stopRequested = true;  
  24.     }  
  25. }  

 以服务器模式运行后会在1秒左右后自动停止VM。

 

除了使用关键字volatile,还可以使用synchronized。同步机制除了能够实现线程之间的互斥之外,还能实现线程间的通信(即可以实现可见性)。

Java代码   收藏代码
  1. import java.util.concurrent.TimeUnit;  
  2.   
  3. // Broken! - How long would you expect this program to run ?  
  4. public class StopThread {  
  5.   
  6.     private static boolean stopRequested;  // value: false  
  7.       
  8.     public static synchronized void requestStop() {  
  9.         stopRequested = true;  
  10.     }  
  11.     public static synchronized boolean stopRequested() {  
  12.         return stopRequested;  
  13.     }  
  14.     public static void main(String... args) throws InterruptedException {  
  15.           
  16.         Thread backgroundThread = new Thread(new Runnable() {  
  17.               
  18.             @Override  
  19.             public void run() {  
  20.                 int i = 0;  
  21.                 while(!stopRequested())  
  22.                     i++;  
  23.             }  
  24.         });  
  25.           
  26.         backgroundThread.start();  
  27.           
  28.         TimeUnit.SECONDS.sleep(1);  
  29.         requestStop();  
  30.     }  
  31. }  

 此时可以且应该取消volatile关键字,因为synchronized关键字也能实现可见性。

另外需要注意的是除了同步写操作之外,还应该同步读操作。如果读和写操作没有都被同步,同步就不会起作用。

你可能感兴趣的:(java进阶)