`

并发--Effective Java的一小段代码

 
阅读更多
import java.util.concurrent.TimeUnit;

/**
 * @author gl65293
 *
 */
public class StopThread {

    /**
     * @param args
     */
    
    private static boolean stopRequested;
    
    public static void main(String[] args) throws InterruptedException {
    
        Thread backgroundThread = new Thread(new Runnable(){
            public void run(){
                int i = 0;
                while(!stopRequested){
                    i++;
                }
            }
        });
        
        backgroundThread.start();
        
        TimeUnit.SECONDS.sleep(1);
        stopRequested = true;
    }

}

在Effective Java 中文版 第二版的230页,说的是这个程序不会停下来. 可你把这个程序放入到eclipse这种ide里去执行的时候,你却发现在很多时候,这个程序都会在执行1秒后停下来. 为什么呢? 这个怎么跟书上说的不一样?

其实, 书上跟自己的实践都是对的, 在eclipse ide里执行这个程序里, 你要加上 -server参数, 这个程序运行就不会停下来了.

这是为什么呢?
因为在加上 -server参数后, jvm会对执行顺序进行优化.

解释如下:
backgroundThread什么时候退出循环是不能确定的,有可能1秒后就退出了,也有可能永远不会退出。程序执行时有如下2中可能情况:
第一种可能,由于stopRequested没有同步或者volatile修饰,jvm为了加快线程的执行速度,每个线程都会对stopRequested做变量拷贝,这时候对变量的修改可能就不会或者不能够及时同步到每个线程中,这种情况backgroundThread 线程什么时候执行结束是不能确定的,>=1秒后结束,或者永远不会结束都有可能。

第二种可能,jvm会对
                while(!stopRequested){
                    i++;
                }



做优化,优化后程序执行流程变为
          if(!stopRequested) {
                while(true){
                    i++;
                }
           }


,这种情况,backgroundThread线程永远不会结束。

为了避免以上两种情况,可以用volatile修饰stopRequested,这样每个线程在修改stopRequested后都会把修改结果同步到主内存,每个线程在读取stopRequested时都会从主内存中获取;volatile同时也阻止了jvm对第二种情况的优化

建议细看: 《java并发编程实践》

注:
在不加 volatile修饰, 把i++改成System.out.println("t"); 的情况下, 使用-server, -client参数都会在1秒后退出.

在windows下,jvm默认是client模式, 在linux下默认是 server模式.

参考:
Java线程安全兼谈DCL
用happen-before规则重新审视DCL
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics