锁定老帖子 主题:Effective Java的一小段代码
精华帖 (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 |
|
返回顶楼 | |
发表时间: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模型都和现在的不同了。细说会有一大堆了。 |
|
返回顶楼 | |
发表时间: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并发编程实践》。 |
|
返回顶楼 | |
发表时间:2011-11-24
LS大师正解啊!!
|
|
返回顶楼 | |
发表时间: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应该交替获得时间片执行吧?? |
|
返回顶楼 | |
发表时间:2011-11-24
panggezi 写道 Cross_Lee 写道 看书看到这一段代码,书上的意思这个backgroundThread永远不会终止,但是我在机器上跑了下,发现1秒之后程序停止了。我的理解是main线程结束,分出来的子线程都跟着结束。为什么书上说永远不会结束。而且我实验了下是会终止的。有知道的解释下,谢谢各位!
有书的可以看Effective Java 中文版 第二版的230页。 不好意思,因为没仔细看楼主的文字,我误解了楼主的意思,“main线程结束,分出来的子线程都跟着结束”这是最基本概念的错误。 晕,我一直以为是这样的。好像在哪看到过这样的观点,我验证了下发现我的观点是错误的,感谢指正,谢谢! 引用 主线程和自己创建的线程是并发的,当主线程结束了的时候,自己建的线程也就结束了。 这个结论好像是不正确的,可以验证下。 |
|
返回顶楼 | |
发表时间: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那句注释了,程序就不会退出了。呵呵。 |
|
返回顶楼 | |
发表时间: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次,基本上程序都会退出。诶 |
|
返回顶楼 | |
发表时间:2011-11-24
最后修改:2011-11-24
nicklasy 写道 backgroundThread什么时候退出循环是不能确定的,有可能1秒后就退出了,也有可能永远不会退出。
但实际测试情况是stopRequested = true;这条语句后backgroundThread就退出了,重复测试N次都是这个结果,说明不是不确定的,而是确定的。 nicklasy 写道 第二种可能,jvm会对
while(!stopRequested){ i++; }做优化,优化后程序执行流程变为 if(!stopRequested) { while(true){ i++; } },这种情况,backgroundThread线程永远不会结束。 看编译后的字节码,不会出现这种优化。 ![]() 代码稍有改动。 |
|
返回顶楼 | |
发表时间:2011-11-24
Cross_Lee 写道 我把后面=true那句注释了,程序就不会退出了。呵呵。
这个是常识吧。JVM终止的条件是“除守护线程外的所有线程都结束,则虚拟机就会退出。” |
|
返回顶楼 | |