失败和成功之间距离只有1M

What you think decides everything

导航

多线程编程 实践篇(五)注:转

Posted on 2008-10-07 16:02  frank.net  阅读(315)  评论(0编辑  收藏  举报

中断线程

有了上一节[线程的中断],我们就好进行如何[中断线程]了.这绝对不是玩一个文字游戏.
是因为"线程的中断"并不能保证"中断线程",所以我要特别地分为两节来说明.

这里说的"中断线程"意思是"停止线程",而为什么不用"停止线程"这个说法呢?

因为线程有一个明确的stop方法,但它是反对使用的,所以请大家记住,在java中以后不要提
停止线程这个说法,忘记它!

 但是,作为介绍线程知识的我,我仍然要告诉你为什么不用"停止线程"的理由.

[停止线程]
 当在一个线程对象上调用stop()方法时,这个线程对象所运行的线程就会立即停止,
并抛出特殊的ThreadDeath()异常.这里的"立即"因为太"立即"了,就象一个正在摆弄自己的
玩具的孩子,听到大人说快去睡觉去,就放着满地的玩具立即睡觉去了.这样的孩子是不乖的.

假如一个线程正在执行:

synchronized void {
 x = 3;
 y = 4;
}
由于方法是同步的,多个线程访问时总能保证x,y被同时赋值,而如果一个线程正在执行到
x = 3;时,被调用了 stop()方法,即使在同步块中,它也干脆地stop了,这样就产生了不完整
的残废数据.而多线程编程中最最基础的条件要保证数据的完整性,所以请忘记线程的stop
方法,以后我们再也不要说"停止线程"了.

 如何才能"结束"一个线程?
[中断线程]
 结束一个线程,我们要分析线程的运行情况.也就是线程正在干什么.如果那个孩子
什么事也没干,那就让他立即去睡觉.而如果那个孩子正在摆弄他的玩具,我们就要让它把玩
具收拾好再睡觉.

 所以一个线程从运行到真正的结束,应该有三个阶段:

 1.正常运行.
 2.处理结束前的工作,也就是准备结束.
 3.结束退出.

 在我的JDBC专栏中我N次提醒在一个SQL逻辑结束后,无论如何要保证关闭Connnection
那就是在finally从句中进行.同样,线程在结束前的工作应该在finally中来保证线程退出前
一定执行:

 try{
  正在逻辑
 }catch(){}
 finally{
  清理工作
 }

那么如何让一个线程结束呢?既然不能调用stop,可用的只的interrupt()方法.但interrupt()
方法只是改变了线程的运行状态,如何让它退出运行?

 对于一般逻辑,只要线程状态为已经中断,我们就可以让它退出,所以这样的语句可以保证
线程在中断后就能结束运行:

 while(!isInterrupted()){
  正常逻辑
 }
 这样如果这个线程被调用interrupt()方法,isInterrupted()为true,就会退出运行.但是
如果线程正在执行wait,sleep,join方法,你调用interrupt()方法,这个逻辑就不完全了.

 如果一个有经验的程序员来处理线程的运行的结束:

 public void run(){
  try{
   while(!isInterrupted()){
    正常工作
   }
  }
  catch(Exception e){
   return;
  }
  finally{
   清理工作
  }
  
 }
我们看到,如果线程执行一般逻辑在调用innterrupt后.isInterrupted()为true,退出循环后执行
清理工作后结束,即使线程正在wait,sleep,join,也会抛出异常执行清理工作后退出.
这看起来非常好,线程完全按最我们设定的思路在工作.但是,并不是每个程序员都有这种认识,如果
他聪明的自己处理异常会如何?事实上很多或大多数程序员会这样处理:

 public void run(){
  
  while(!isInterrupted()){
   try{
    正常工作
   }catch(Exception e){
    //nothing
   }
   finally{
   
   }
  }
 } 

 想一想,如果一个正在sleep的线程,在调用interrupt后,会如何?
 wait方法检查到isInterrupted()为true,抛出异常,而你又没有处理.而一个抛出了
InterruptedException的线程的状态马上就会被置为非中断状态,如果catch语句没有处理异常,则
下一次循环中isInterrupted()为false,线程会继续执行,可能你N次抛出异常,也无法让线程停止.

 那么如何能确保线程真正停止?
 在线程同步的时候我们有一个叫"二次惰性检测"(double check),能在提高效率的基础上又
确保线程真正中同步控制中.
 那么我把线程正确退出的方法称为"双重安全退出",即不以isInterrupted()为循环条件.而
以一个标记作为循环条件:

class MyThread extend Thread{
 private boolean isInterrupted = false;//这一句以后要修改

 public void interrupt(){
  isInterrupted = true;
  super.interrupt();
 }
 public void run(){
  
  while(!isInterrupted){
   try{
    正常工作
   }catch(Exception e){
    //nothing
   }
   finally{
   
   }
  }
 }
}

试试这段程序,可以正确工作吗?
对于这段程序仍然还有很多可说的地方,先到这里吧.