




已阅读5页,还剩18页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
终止一个线程的方法 分类: java多线程 2008-11-14 10:39 147人阅读 评论(0) 收藏 举报 转自:/p69431. importjava.util.*; 2.3. publicclassT 4. staticbooleanrunning=true; 5.6. publicstaticvoidmain(Stringagrs) 7. Threada=newThread(newRunnable() 8. publicvoidrun() 9. try 10. while(running) 11. Thread.sleep(1000); 12. System.out.println(#+newDate()+#); 13. 14. catch(InterruptedExceptione) 15. return; 16. 17. 18. ); 19. a.start(); 20. try 21. Thread.sleep(10000); 22. catch(InterruptedExceptione) 23. running=false;/1此方法使用标志进行,推荐使用 24. /a.stop();/2此方法已经不推荐使用了 25. 26. 来源于:转载自dev2dev网友axman的go deep into java专栏。不客气地说,至少有一半人认为,线程的“中断”就是让线程停止。如果你也这么认为,那你对多线程编程还没有入门。 在java中,线程的中断(interrupt)只是改变了线程的中断状态,至于这个中断状态改变后带来的结果,那是无法确定的,有时它更是让停止中的线程继续执行的唯一手段。不但不是让线程停止运行,反而是继续执行线程的手段。 对于执行一般逻辑的线程,如果调用它的interrupt()方法,那么对这个线程没有任何影响,比如线程a正在执行:while(条件) x +;这样的语句,如果其它线程调用errupt();那么并不会影响a对象上运行的线程,如果在其它线程里测试a的中断状态它已经改变,但并不会停止这个线程的运行。在一个线程对象上调用interrupt()方法,真正有影响的是wait,join,sleep方法,当然这三个方法包括它们的重载方法。 请注意:上面这三个方法都会抛出InterruptedException,记住这句话,下面我会重复。一个线程在调用interrupt()后,自己不会抛出InterruptedException异常,所以你看到interrupt()并没有抛出这个异常,所以我上面说如果线程a正在执行while(条件) x +;你调用errupt();后线程会继续正常地执行下去。 但是,如果一个线程被调用了interrupt()后,它的状态是已中断的。这个状态对于正在执行wait,join,sleep的线程,却改变了线程的运行结果。 一、对于wait中等待notify/notifyAll唤醒的线程,其实这个线程已经“暂停”执行,因为它正在某一对象的休息室中,这时如果它的中断状态被改变,那么它就会抛出异常。这个InterruptedException异常不是线程抛出的,而是wait方法,也就是对象的wait方法内部会不断检查在此对象上休息的线程的状态,如果发现哪个线程的状态被置为已中断,则会抛出InterruptedException,意思就是这个线程不能再等待了,其意义就等同于唤醒它了。 这里唯一的区别是,被notify/All唤醒的线程会继续执行wait下面的语句,而在wait中被中断的线程则将控制权交给了catch语句。一些正常的逻辑要被放到catch中来运行。但有时这是唯一手段,比如一个线程a在某一对象b的wait中等待唤醒,其它线程必须获取到对象b的监视锁才能调用b.notify()All,否则你就无法唤醒线程a,但在任何线程中可以无条件地调用errupt();来达到这个目的。只是唤醒后的逻辑你要放在catch中,当然同notify/All一样,继续执行a线程的条件还是要等拿到b对象的监视锁。 二、对于sleep中的线程,如果你调用了Thread.sleep(一年);现在你后悔了,想让它早些醒过来,调用interrupt()方法就是唯一手段,只有改变它的中断状态,让它从sleep中将控制权转到处理异常的catch语句中,然后再由catch中的处理转换到正常的逻辑。同样地,于join中的线程你也可以这样处理。 对于一般介绍多线程模式的书上,他们会这样来介绍:当一个线程被中断后,在进入wait,sleep,join方法时会抛出异常。是的,这一点也没有错,但是这有什么意义呢?如果你知道那个线程的状态已经处于中断状态,为什么还要让它进入这三个方法呢?当然有时是必须这么做的,但大多数时候没有这么做的理由,所以我上面主要介绍了在已经调用这三个方法的线程上调用interrupt()方法让它从暂停状态中恢复过来。这个恢复过来就可以包含两个目的: 一、可以使线程继续执行,那就是在catch语句中招待醒来后的逻辑,或由catch语句转回正常的逻辑。总之它是从wait,sleep,join的暂停状态活过来了。 二、可以直接停止线程的运行,当然在catch中什么也不处理,或return,那么就完成了当前线程的使命,可以使在上面“暂停”的状态中立即真正的“停止”。 中断线程 有了上一节线程的中断,我们就好进行如何中断线程了。这绝对不是玩一个文字游戏。是因为“线程的中断”并不能保证“中断线程”,所以我要特别地分为两节来说明。这里说的“中断线程”意思是“停止线程”,而为什么不用“停止线程”这个说法呢?因为线程有一个明确的stop方法,但它是反对使用的,所以请大家记住,在java中以后不要提停止线程这个说法,忘记它!但是,作为介绍线程知识的我,我仍然要告诉你为什么不用“停止线程”的理由。 停止线程 当在一个线程对象上调用stop()方法时,这个线程对象所运行的线程就会立即停止,并抛出特殊的ThreadDeath()异常。这里的“立即”因为太“立即”了,就象一个正在摆弄自己的玩具的孩子,听到大人说快去睡觉去,就放着满地的玩具立即睡觉去了。这样的孩子是不乖的。 假如一个线程正在执行: 1. synchronizedvoid 2. x=3; 3. y=4; 4. 5.由于方法是同步的,多个线程访问时总能保证x,y被同时赋值,而如果一个线程正在执行到x = 3;时,被调用了 stop()方法,即使在同步块中,它也干脆地stop了,这样就产生了不完整的残废数据。而多线程编程中最最基础的条件要保证数据的完整性,所以请忘记线程的stop方法,以后我们再也不要说“停止线程”了。 如何才能“结束”一个线程? 中断线程 结束一个线程,我们要分析线程的运行情况。也就是线程正在干什么。如果那个孩子什么事也没干,那就让他立即去睡觉。而如果那个孩子正在摆弄他的玩具,我们就要让它把玩具收拾好再睡觉。 所以一个线程从运行到真正的结束,应该有三个阶段: 1. 正常运行. 2. 处理结束前的工作,也就是准备结束. 3. 结束退出. 在我的JDBC专栏中我N次提醒在一个SQL逻辑结束后,无论如何要保证关闭Connnection那就是在finally从句中进行。同样,线程在结束前的工作应该在finally中来保证线程退出前一定执行: 1. try 2. 正在逻辑 3. catch() 4. finally 5. 清理工作 6. 那么如何让一个线程结束呢?既然不能调用stop,可用的只的interrupt()方法。但interrupt()方法只是改变了线程的运行状态,如何让它退出运行?对于一般逻辑,只要线程状态已经中断,我们就可以让它退出,所以这样的语句可以保证线程在中断后就能结束运行: 1. while(!isInterrupted() 2. 正常逻辑 3. 这样如果这个线程被调用interrupt()方法,isInterrupted()为true,就会退出运行。但是如果线程正在执行wait,sleep,join方法,你调用interrupt()方法,这个逻辑就不完全了。 如果一个有经验的程序员来处理线程的运行的结束: 1. publicvoidrun() 2. try 3. while(!isInterrupted() 4. 正常工作 5. 6. 7. catch(Exceptione) 8. return; 9. 10. finally 11. 清理工作 12. 13. 我们看到,如果线程执行一般逻辑在调用innterrupt后,isInterrupted()为true,退出循环后执行清理工作后结束,即使线程正在wait,sleep,join,也会抛出异常执行清理工作后退出。 这看起来非常好,线程完全按最我们设定的思路在工作。但是,并不是每个程序员都有这种认识,如果他聪明的自己处理异常会如何?事实上很多或大多数程序员会这样处理: 1. publicvoidrun() 2. while(!isInterrupted() 3. try 4. /正常工作 5. catch(Exceptione) 6. /nothing 7. finally 8. 9. 10. 想一想,如果一个正在sleep的线程,在调用interrupt后,会如何?wait方法检查到isInterrupted()为true,抛出异常,而你又没有处理。而一个抛出了InterruptedException的线程的状态马上就会被置为非中断状态,如果catch语句没有处理异常,则下一次循环中isInterrupted()为false,线程会继续执行,可能你N次抛出异常,也无法让线程停止。 那么如何能确保线程真正停止?在线程同步的时候我们有一个叫“二次惰性检测”(double check),能在提高效率的基础上又确保线程真正中同步控制中。那么我把线程正确退出的方法称为“双重安全退出”,即不以 isInterrupted()为循环条件。而以一个标记作为循环条件: 1. classMyThreadextendsThread 2. privatebooleanisInterrupted=false;/这一句以后要修改 3. publicvoidinterrupt() 4. isInterrupted=true; 5. errupt(); 6. 7. publicvoidrun() 8. while(!isInterrupted) 9. try 10. /正常工作 11. catch(Exceptione) 12. /nothing 13. finally 14. 15. 16. 17. 试试这段程序,可以正确工作吗? 对于这段程序仍然还有很多可说的地方,先到这里吧。深入了解线程对象与线程,线程与运行环境 在基础篇中的第一节,我就强调过,要了解多线程编程,首要的两个概念就是线程对象和线程。现在我们来深入理解线程对象,线程,运行环境之间的关系,弄清Runnable与Thread的作用。 在JAVA平台中,序列化机制是一个非常重要的机制,如果不能理解并熟练应用序列化机制,你就不能称得上一个java程序员。 在JAVA平台中,为什么有些对象中可序列化的,而有些对象就不能序列化? 能序列化的对象,简单说是一种可以复制(意味着可以按一定机制进行重构它)的对象,这种对象说到底就是内存中一些数据的组合。只要按一定位置和顺序组合就能完整反映这个对象。 而有些对象,是和当前环境相关的,它反映了当前运行的环境和时序,所以不能被序列,否则在另外的环境和时序中就无法“还原”。 比如,一个Socket对象: 1. Socketsc=newSocket(11,80);这个sc对象表示当前正在运行这段代码的主机和IP为11的80端口之间建立的一个物理连结,如果它被序列化,那么在另一个时刻在另一个主机上它如何能被还原?Socket连结一旦断开,就已经不存在,它不可能在另一个时间被另一个主机所重现。重现的已经不是原来那个sc对象了。 线程对象也是这种不可序列化对象,当我们new Thread时,已经初始化了当前这个线程对象所在有主机的运行环境相关的信息,线程调度机制,安全机制等只特定于当前运行环境的信息,假如它被序列化,在另一个环境中运行的时候原来初始化的运行环境的信息就不可能在新的环境中运行。而假如要重新初始化,那它已经不是原来那个线程对象了。 正如Socket封装了两个主机之间的连结,但它们并不是已经连结关传送数据了。要想传送数据,你还要getInputStream和getOutputStream,并read和write,两台主机之间才开始真正的“数据连结”。 一个Thread对象并建立后,只是有了可以运行的令牌,仅仅只是一个线程对象。只有当它调用start()后,当前环境才会分配给它一个运行的空间,让这段代码开始运行。这个运行的空间,才叫真正的线程。也就是说,真正的线程是指当前正在执行的那一个事件。是那个线程对象所在的运行环境。 明白了上面的概念,我们再来看看JAVA中为什么要有Runnable对象和Thread对象。 一、从设计技巧上说,JAVA中为了实现回调,无法调用方法指针,那么利用接口来约束实现者强制提供匹配的方法,并将实现该接口的类的实例作为参数来提供给调用者,这是JAVA平台实现回调的重要手段。 二、但是从实际的操作来看,对于算法和数据,是不依赖于任何环境的。所以把想要实现的操作中的算法和数据封装到一个run方法中(由于算法本身是数据的一个部分,所以我把它们合并称为数据),可以将离数据和环境的逻辑分离开来。使程序员只关心如何实现我想做的操作,而不要关心它所在的环境。当真正的需要运行的时候再将这段操作传给一个具体当前环境的Thread对象。 三、这是最最重要的原因:实现数据共享 因为一个线程对象不对多次运行。所以把数据放在Thread对象中,不会被多个线程同时访问。简单说: 1. classTextendsThread 2. Objectx; 3. publicvoidrun()/.; 4. 5. 6. 7. Tt=newT();当T的实例t运行后,t所包含的数据x只能被一个t.start();对象共享,除非声明成 static Object x; 一个t的实例数据只能被一个线程访问。意思是一个数据实例对应一个线程。 而假如我们从外部传入数据,比如 1. classTextendsThread 2. privateObjectx; 3. publicT(Objectx) 4. this.x=x; 5. 6. 7. publicvoidrun()/.; 8. 这样我们就可以先生成一个x对象传给多个Thread对象,多个线程共同操作一个数据。也就是一个数据实例对应多个线程。 现在我们把数据更好地组织一下,把要操作的数据Object x和要进行的操作一个封装到Runnable的run()方法中,把Runnable实例从外部传给多个Thread对象。这样,我们就有了: 一个对象的多个线程 这是以后我们要介绍的线程池的重要概念。java多线程编程 - 实战篇(二) 分类: java多线程 2008-11-13 15:54 158人阅读 评论(0) 收藏 举报 java多线程编程 - 实战篇(二)来源于:转载自dev2dev网友axman的go deep into java专栏。 发布时间:2007-12-10 16:58:24 本节继续上一节的讨论。 一个线程在进入对象的休息室(调用该对象的wait()方法)后会释放对该对象的锁,基于这个原因。在同步中,除非必要,否则你不应用使用Thread.sleep(long l)方法,因为sleep方法并不释放对象的锁。 这是一个极其恶劣的品德,你自己什么事也不干,进入sleep状态,却抓住竞争对象的监视锁不让其它需要该对象监视锁的线程运行,简单说是极端自私的一种行为。但我看到过很多程序员仍然有在同步方法中调用sleep的代码。 看下面的例子: 1. classSleepTest 2. publicsynchronizedvoidwantSleep() 3. try 4. Thread.sleep(1000*60); 5. catch(Exceptione) 6. 7. System.out.println(111); 8. 9. publicsynchronizedvoidsay() 10. System.out.println(123); 11. 12. 13.14. classT1extendsThread 15. SleepTestst; 16. publicT1(SleepTestst) 17. this.st=st; 18. 19. publicvoidrun() 20. st.wantSleep(); 21. 22. 23.24. classT2extendsThread 25. SleepTestst; 26. publicT2(SleepTestst) 27. this.st=st; 28. 29. publicvoidrun() 30. st.say(); 31. 32. 33.34. publicclassTest 35. publicstaticvoidmain(Stringargs)throwsException 36. SleepTestst=newSleepTest(); 37. newT1(st).start(); 38. newT2(st).start(); 39. 40. 我们看到,线程T1的实例运行后,当前线程抓住了st实例的锁,然后进入了sleep。直到它睡满60秒后才运行到System.out.println(111);然后run方法运行完成释放了对st的监视锁,线程T2的实例才得到运行的机会。 而如果我们把wantSleep方法改成:1. publicsynchronizedvoidwantSleep() 2. try 3. /Thread.sleep(1000*60); 4. this.wait(1000*60); 5. catch(Exceptione) 6. System.out.println(111); 7. 我们看到,T2的实例所在的线程立即就得到了运行机会,首先打印了123,而T1的实例所在的线程仍然等待,直到等待60秒后运行到System.out.println(111);方法。 所以,调用wait(long l)方法不仅达到了阻塞当前线程规定时间内不运行,而且让其它有竞争需求的线程有了运行机会,这种利人不损己的方法,何乐而不为?这也是一个有良心的程序员应该遵循的原则。 当一个线程调用wait(long l)方法后,线程如果继续运行,你无法知道它是等待时间完成了还是在wait时被其它线程唤醒了,如果你非常在意它一定要等待足够的时间才执行某任务,而不希望是中途被唤醒,这里有一个不是非常准确的方法: 1. longl=System.System.currentTimeMillis(); 2.3. wait(1000);/准备让当前线程等待1秒 4. while(System.System.currentTimeMillis()-l)=this.maxSize) 3. try 4. this.wait(); 5. catch(Exceptione) 6. 7. 8. this.add(f); 9. notifyAll(); 10. 拿菜:同上面,如果桌子上一盘菜也没有,所有食客都要等待: 1. publicsynchronizedFoodgetFood() 2. while(this.size()=this.maxSize) 15. try 16. this.wait(); 17. catch(Exceptione) 18. 19. 20. this.add(f); 21. notifyAll(); 22. 23. publicsynchronizedFoodgetFood() 24. while(this.size()=0) 25. try 26. this.wait(); 27. catch(Exceptione) 28. 29. 30. Foodf=(Food)this.removeFirst(); 31. notifyAll(); 32. returnf; 33. 34. 35.36. classChefextendsThread 37. Tablet; 38. Stringname; 39. Randomr=newRandom(12345); 40. publicChef(Stringname,Tablet) 41. this.t=t; 42. =name; 43. 44. publicvoidrun() 45. while(true) 46. Foodf=make(); 47. System.out.println(name+putaFood:+f); 48. t.putFood(f); 49. 50. 51. privateFoodmake() 52. try 53. Thread.sleep(200+r.nextInt(200); 54. catch(Exceptione) 55. 56. returnnewFood(); 57. 58. 59.60. classEaterextendsThread 61. Tablet; 62. Stringname; 63. Randomr=newRandom(54321); 64. publicEater(Stringname,Tablet) 65. this.t=t; 66. this.nam
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 入职安全教育培训理解课件
- 2024年山西警察学院军训动员大会校长发言稿9000字
- 龙胜县裕朗滑石制品有限公司项目建设项目环境影响报告表
- 2025年福建省“超级全能生”物理高三第一学期期末检测试题
- 防疫管理办法解说稿
- 企业现场安全知识培训
- 企业春季安全教育培训课件
- 环境监察培训管理办法
- 智能语音导览系统-洞察及研究
- 至阴穴脑功能重塑-洞察及研究
- 建筑安全员c2考试题库及答案
- 2025广东惠州惠城区招聘社区工作站工作人员66人笔试备考试题及答案解析
- 洋务运动和边疆危机课件-2025-2026学年统编版八年级历史上册
- 2025年中学教师资格考试《综合素质》核心考点特训题库(含答案)之教育文化素养论述题库
- 部编高教版2023·职业模块 中职语文 2.《宁夏闽宁镇:昔日干沙滩今日金沙滩》 课件
- 新教师跟岗学习实施方案
- 2022年高考全国甲卷:写作指导及范文课件16张
- 郭锡良《古代汉语》讲稿(不仔细看别后悔哦)
- 新媒体文案创作与传播精品课件(完整版)
- 齿轮制造工艺手册
- 8D培训教材(共37页).ppt
评论
0/150
提交评论