《Effective Java》的并发章节讲了一个线程通信问题的例子,非常好的说明了如何由于失误代码导致的线程间通信失败,没有保证一个线程的所作的修改可以被另一个线程获知,也就是活性失败和安全性失败。回头想想,这样的失误(或者根本就不是失误,而是程序员们功力不到家)在工作中太常见了,特别是如果线上系统存在这样的代码,简直就是埋下了地雷。
作者的例子如下(有改动,但不影响):
private static boolean stopRequested;
public static void main(){
Thread t = new Thread(new Runnable(){
public void run(){
int i = 0;
while(!stopRequested){
++i;
}
}
});
t .start();
TimeUnit.SECOND.sleep(1);
stopRequested = true;
}
程序员的本意是希望设置 stopRequested = true 后,线程 t 能退出,但并不能达到预期效果。因为类的 static 变量虽然对 Java 的多个对象而言就是唯一的一份拷贝,无论创建了多少个对象。但对于线程,static 变量跟普通变量一样,每个线程自己缓存了一份本地拷贝,t1 改变了变量的值,对于 t2 并不可见,也就是单纯地定义变量为 static 还不够,必须同时声明为 volatile (或者同步锁改变 stopRequested 的值),这样 t1 跟 t2 才能正常通信(针对上述例子而言)。
这篇文章本意是讲一讲 static 跟 volatile 。so,现在问题来了,如果只声明 stopRequested 为 volatile ,上述的问题还会存在吗?答案是肯定的。定义为 volatile ,跟普通变量一样,是每个线程的一个本地拷贝,线程间并不能感知变量值的变化。声明为 static volatile,会迫使线程每次读取时作为一个全局变量读取。
网友评论