论坛首页 Java企业应用论坛

Effective Java的一小段代码

浏览 16798 次
精华帖 (0) :: 良好帖 (1) :: 新手帖 (5) :: 隐藏帖 (0)
作者 正文
   发表时间:2011-11-24   最后修改:2011-11-24
Cross_Lee 写道
看书看到这一段代码,书上的意思这个backgroundThread永远不会终止,但是我在机器上跑了下,发现1秒之后程序停止了。我的理解是main线程结束,分出来的子线程都跟着结束。为什么书上说永远不会结束。而且我实验了下是会终止的。有知道的解释下,谢谢各位!

有书的可以看Effective Java 中文版 第二版的230页。

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;
    }

}




这个问题涉及到Java内存模型,线程工作内存和主内存数据同步问题。
看代码,应该是程序不会终止才对。因为在backgroundThread中,对stopRequested 变量的读取,并没有添加同步条件,按JVM关于数据同步的规则,JVM不保证backgroundThread线程可以看到main线程对stopRequested 变量的修改,也就是说backgroundThread线程中看到的永远只是stopRequested 的初始值才对。
但实际在JDK1.4、JDK5、JDK6下分别测试的结果,却是主线程结束后,程序也结束了,很奇怪。

Cross_Lee 写道
书上的意思是:给这个stopRequested 用synchronized包起来,那个backgroundThread就会停止了。但是我跑1L的代码还是会停止。

这个说的就是上面的意思。JVM保证在同步块进入和退出时,会去读取主内存的数据到线程工作内存,这样backgroundThread线程才会看到stopRequested 变量的最新修改值。

楼主可以参考一下:
http://www.iteye.com/topic/875420
http://www.iteye.com/topic/260515


0 请登录后投票
   发表时间:2011-11-24  
LZ,你可以试注释掉
stopRequested = true;这一行
怎么样,是不是不停了?
原因其实很简单,当你new出一个新线程后,这个backgroundThread和原来mian进程是同时并发执行的,但是backgroundThread中的循环是否继续执行是根据main中的stopRequested 值进行的,也就是说,这个值改了就停止while循环了。
之所以出现你说的一秒就结束,你可以试试TimeUnit.SECONDS.sleep(1);  这里改成
TimeUnit.SECONDS.sleep(2);
TimeUnit.SECONDS.sleep(10);
现在明白了吧?
具体到书中的意思是,当backgroundThread运行不停止,main是不会继续运行的。这个涉及到作者写代码的年代了,那个时候的CPU和java模型都和现在的不同了。细说会有一大堆了。
0 请登录后投票
   发表时间:2011-11-24   最后修改:2011-11-24
Cross_Lee 写道
看书看到这一段代码,书上的意思这个backgroundThread永远不会终止,但是我在机器上跑了下,发现1秒之后程序停止了。我的理解是main线程结束,分出来的子线程都跟着结束。为什么书上说永远不会结束。而且我实验了下是会终止的。有知道的解释下,谢谢各位!

有书的可以看Effective Java 中文版 第二版的230页。

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;
    }

}




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并发编程实践》。
2 请登录后投票
   发表时间:2011-11-24  
LS大师正解啊!!
0 请登录后投票
   发表时间:2011-11-24  
pxlfxl2 写道
Cross_Lee 写道
看书看到这一段代码,书上的意思这个backgroundThread永远不会终止,但是我在机器上跑了下,发现1秒之后程序停止了。我的理解是main线程结束,分出来的子线程都跟着结束。为什么书上说永远不会结束。而且我实验了下是会终止的。有知道的解释下,谢谢各位!

有书的可以看Effective Java 中文版 第二版的230页。

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;
    }

}




java线程不是抢占式模型,是协同式线程模型,这段程序如果在单核单线程cpu下应该不会停止的,因为main线程没有执行的机会,stopRequested = true这个语句也就不会执行。不过在多核cpu下就不一定了,主线程和backgroundThread可能会同时执行。


单核下,main线程和backgroundThread应该交替获得时间片执行吧??
0 请登录后投票
   发表时间:2011-11-24  
panggezi 写道
Cross_Lee 写道
看书看到这一段代码,书上的意思这个backgroundThread永远不会终止,但是我在机器上跑了下,发现1秒之后程序停止了。我的理解是main线程结束,分出来的子线程都跟着结束。为什么书上说永远不会结束。而且我实验了下是会终止的。有知道的解释下,谢谢各位!

有书的可以看Effective Java 中文版 第二版的230页。

不好意思,因为没仔细看楼主的文字,我误解了楼主的意思,“main线程结束,分出来的子线程都跟着结束”这是最基本概念的错误。


晕,我一直以为是这样的。好像在哪看到过这样的观点,我验证了下发现我的观点是错误的,感谢指正,谢谢!



引用

主线程和自己创建的线程是并发的,当主线程结束了的时候,自己建的线程也就结束了。


这个结论好像是不正确的,可以验证下。
0 请登录后投票
   发表时间:2011-11-24  
引用

这个问题涉及到Java内存模型,线程工作内存和主内存数据同步问题。
看代码,应该是程序不会终止才对。因为在backgroundThread中,对stopRequested 变量的读取,并没有添加同步条件,按JVM关于数据同步的规则,JVM不保证backgroundThread线程可以看到main线程对 stopRequested 变量的修改,也就是说backgroundThread线程中看到的永远只是stopRequested 的初始值才对。
但实际在JDK1.4、JDK5、JDK6下分别测试的结果,却是主线程结束后,程序也结束了,很奇怪。

Cross_Lee 写道
书上的意思是:给这个stopRequested 用synchronized包起来,那个backgroundThread就会停止了。但是我跑1L的代码还是会停止。

这个说的就是上面的意思。JVM保证在同步块进入和退出时,会去读取主内存的数据到线程工作内存,这样backgroundThread线程才会看到stopRequested 变量的最新修改值。

楼主可以参考一下:
http://www.iteye.com/topic/875420
http://www.iteye.com/topic/260515


感谢,参考的讲的很好,对我理解很有帮助。谢谢!

引用
但实际在JDK1.4、JDK5、JDK6下分别测试的结果,却是主线程结束后,程序也结束了,很奇怪。

不过还是想不通这个为什么会结束。
我把后面=true那句注释了,程序就不会退出了。呵呵。
0 请登录后投票
   发表时间:2011-11-24  
nicklasy 写道
Cross_Lee 写道
看书看到这一段代码,书上的意思这个backgroundThread永远不会终止,但是我在机器上跑了下,发现1秒之后程序停止了。我的理解是main线程结束,分出来的子线程都跟着结束。为什么书上说永远不会结束。而且我实验了下是会终止的。有知道的解释下,谢谢各位!

有书的可以看Effective Java 中文版 第二版的230页。

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;
    }

}




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并发编程实践》。


恩,目前只能这么理解了,感谢!

引用
backgroundThread 线程什么时候执行结束是不能确定的,>=1秒后结束,或者永远不会结束都有可能。

关于这个,我跑了不下20次,基本上程序都会退出。诶
0 请登录后投票
   发表时间:2011-11-24   最后修改:2011-11-24
nicklasy 写道
backgroundThread什么时候退出循环是不能确定的,有可能1秒后就退出了,也有可能永远不会退出。

但实际测试情况是stopRequested = true;这条语句后backgroundThread就退出了,重复测试N次都是这个结果,说明不是不确定的,而是确定的。

nicklasy 写道
第二种可能,jvm会对
                while(!stopRequested){
                    i++;
                }
做优化,优化后程序执行流程变为
          if(!stopRequested) {
                while(true){
                    i++;
                }
           }
,这种情况,backgroundThread线程永远不会结束。

看编译后的字节码,不会出现这种优化。


代码稍有改动。
  • 大小: 13.1 KB
0 请登录后投票
   发表时间:2011-11-24  
Cross_Lee 写道
我把后面=true那句注释了,程序就不会退出了。呵呵。

这个是常识吧。JVM终止的条件是“除守护线程外的所有线程都结束,则虚拟机就会退出。”

0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics