多线程编程——实战篇(四)

多线程编程——实战篇(一)


多线程编程——实战篇(二)

多线程编程——实战篇(三)

  不客气地说,至少有一半人认为,线程的“中断”就是让线程停止。如果你也这么认为,那你对多线程编程还没有入门。

  在java中,线程的中断(interrupt)只是改变了线程的中断状态,至于这个中断状态改变后带来的结果,那是无法确定的,有时它更是让停止中的线程继续执行的唯一手段。不但不是让线程停止运行,反而是继续执行线程的手段。

  对于执行一般逻辑的线程,如果调用它的interrupt()方法,那么对这个线程没有任何影响,比如线程a正在执行:while(条件)  x ++;这样的语句,如果其它线程调用a.interrupt();那么并不会影响a对象上运行的线程,如果在其它线程里测试a的中断状态它已经改 变,但并不会停止这个线程的运行。在一个线程对象上调用interrupt()方法,真正有影响的是wait,join,sleep方法,当然这三个方法 包括它们的重载方法。

  请注意:[上面这三个方法都会抛出InterruptedException],记住这句话,下面我会重 复。一个线程在调用interrupt()后,自己不会抛出InterruptedException异常,所以你看到interrupt()并没有抛出 这个异常,所以我上面说如果线程a正在执行while(条件) x ++;你调用a.interrupt();后线程会继续正常地执行下去。

  但是,如果一个线程被调用了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,但在任何线程中可以无条件地调用a.interrupt();来达到这个目的。只是 唤醒后的逻辑你要放在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()异常。这里的“立 即”因为太“立即”了,就象一个正在摆弄自己的玩具的孩子,听到大人说快去睡觉去,就放着满地的玩具立即睡觉去了。这样的孩子是不乖的。

  假如一个线程正在执行:

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{

}
}
}
}

  试试这段程序,可以正确工作吗?

  对于这段程序仍然还有很多可说的地方,先到这里吧。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值