有关线程中断和线程阻塞

来源:互联网 发布:mysql备份数据库 编辑:程序博客网 时间:2024/06/05 07:53

一个线程都要从运行到结束都要经过3个阶段:

     1、正在运行

     2、准备结束运行

     3、结束运行

那么怎么结束这个线程呢?可以通过下面这三个方法结束一个线程。

     1、使用stop()方法强制结束线程。

     2、使用thread.interrupt()方法发送中断。

     3、在Thread对象中设置共享变量,通过在run方法中不断检测该变量的值来决定是否结束。

第一种方法,stop()方法。 

臭名昭著的stop()停止线程的方法已不提倡使用了,原因是什么呢?当在一个线程对象上调用stop()方法时,这个线程对象所运行的线程就会立即停止,并抛出特殊的ThreadDeath()异常。这里的“立即”因为太“立即”了,假如一个线程正在执行:

[java] view plain copy
  1. synchronized void {  
  2.  x = 3;  
  3.  y = 4;  
  4. }  
由于方法是同步的,多个线程访问时总能保证x,y被同时赋值,而如果一个线程正在执行到x = 3;时,被调用了 stop()方法,即使在同步块中,它也干脆地stop了,这样就产生了不完整

的残废数据。而多线程编程中最最基础的条件要保证数据的完整性,所以请忘记 线程的stop方法,以后我们再也不要说“停止线程”了


第二种方法,中断线程。

每个线程都有一个线程中断标志位来标志该线程是否被中断。我们可以通过检测该标志位是否为true来判断该线程是否被中断。调用线程的thread.interrupt()方法,将会设置该线程为中断状态,即设置为true。线程中断后的结果是死亡、还是等待新的任务或是继续运行至下一步,取决于这个程序本身线程会不时地检测这个中断标识位,以判断线程是否应该被中断(中断标识值是否为true)。它并不像stop方法那样会中断一个正在运行的线程。

当我们调用thread.interrupt()方法时,会将thread的中断标志位设为true。通过isInterrupted()方法可以判断标志位是否为true。此后程序是该继续还是结束就取决于程序本身了。

注意如果一个线程处于了阻塞状态(如线程调用了thread.sleep、thread.join、thread.wait、1.5中的condition.await、以及可中断的通道上的 I/O 操作方法后可进入阻塞状态),则在线程在检查中断标识时如果发现中断标示为true,则会在这些阻塞方法(sleep、join、wait、1.5中的condition.await及可中断的通道上的 I/O 操作方法)调用处抛出InterruptedException异常,并且在抛出异常后立即将线程的中断标别位清除,即重新设置为false。抛出异常是为了线程从阻塞状态醒过来,并在结束线程前让程序员有足够的时间来处理中断请求。一个抛出了InterruptedException的线程的状态马上就会被置为非中断状态,如果catch语句没有处理异常,则下一 次循环中isInterrupted()为false,线程会继续执行,可能你N次抛出异常,也无法让线程停止。例如下面例子:

[java] view plain copy
  1. public class ThreadA extends Thread {  
  2.    int count=0;  
  3.    public void run(){  
  4.        System.out.println(getName()+"将要运行...");  
  5.        while(!this.isInterrupted()){  
  6.            System.out.println(getName()+"运行中"+count++);  
  7.            try{  
  8.                Thread.sleep(400);  
  9.            }catch(InterruptedException e){  
  10.                System.out.println(getName()+"从阻塞中退出...");  
  11.                System.out.println("this.isInterrupted()="+this.isInterrupted());  
  12.            }  
  13.        }  
  14.        System.out.println(getName()+"已经终止!");  
  15.    }  
  16. }  
因为上例中try在while循环里面,当检测到异常时会执行打印语句,当再次进行while(!this.isInterrupted())判定的时候,请注意,由于此时中断标志位已经被重置为false了,所以while循环会一直继续下去。

[java] view plain copy
  1. public class ThreadA extends Thread {  
  2.    int count=0;  
  3.    public void run(){  
  4.        System.out.println(getName()+"将要运行...");  
  5.        while(!this.isInterrupted()){  
  6.            System.out.println(getName()+"运行中"+count++);  
  7.            try{  
  8.                Thread.sleep(400);  
  9.            }catch(InterruptedException e){  
  10.            this.interrupt();  
  11.                System.out.println(getName()+"从阻塞中退出...");  
  12.                System.out.println("this.isInterrupted()="+this.isInterrupted());  
  13.            }  
  14.        }  
  15.        System.out.println(getName()+"已经终止!");  
  16.    }  
  17. }  

比较良好处理方法是:

[java] view plain copy
  1. public void run() {     
  2.     try {     
  3.         /*   
  4.          * 不管循环里是否调用过线程阻塞的方法如sleep、join、wait,这里还是需要加上   
  5.          * !Thread.currentThread().isInterrupted()条件,虽然抛出异常后退出了循环,显   
  6.          * 得用阻塞的情况下是多余的,但如果调用了阻塞方法但没有阻塞时,这样会更安全、更及时。   
  7.          */    
  8.         while (!Thread.currentThread().isInterrupted()&& more work to do) {     
  9.             do more work      
  10.         }     
  11.     } catch (InterruptedException e) {     
  12.         //线程在wait或sleep期间被中断了     
  13.     } finally {     
  14.         //线程结束前做一些清理工作     
  15.     }     
  16. }  

注,synchronized在获锁的过程中是不能被中断的,意思是说如果产生了死锁,则不可能被中断(请参考后面的测试例子)。与synchronized功能相似的reentrantLock.lock()方法也是一样,它也不可中断的,即如果发生死锁,那么reentrantLock.lock()方法无法终止,如果调用时被阻塞,则它一直阻塞到它获取到锁为止。但是如果调用带超时的tryLock方法reentrantLock.tryLock(long timeout, TimeUnit unit),那么如果线程在等待时被中断,将抛出一个InterruptedException异常,这是一个非常有用的特性,因为它允许程序打破死锁。你也可以调用reentrantLock.lockInterruptibly()方法,它就相当于一个超时设为无限的tryLock方法。

另外不要在你的底层代码里捕获InterruptedException异常后不处理,会处理不当,如下:

[java] view plain copy
  1. void mySubTask(){     
  2.     ...     
  3.     try{     
  4.         sleep(delay);     
  5.     }catch(InterruptedException e){}//不要这样做     
  6.     ...     
  7. }  

如果你不知道抛InterruptedException异常后如何处理,那么你有如下好的建议处理方式:
1、在catch子句中,调用Thread.currentThread.interrupt()来设置中断状态(因为抛出异常后中断标示会被清除),让外界通过判断Thread.currentThread().isInterrupted()标示来决定是否终止线程还是继续下去,应该这样做:

[java] view plain copy
  1. void mySubTask() {     
  2.     ...     
  3.     try {     
  4.         sleep(delay);     
  5.     } catch (InterruptedException e) {     
  6.         Thread.currentThread().interrupted();     
  7.     }     
  8.     ...     
  9. }  

2、或者,更好的做法就是,不使用try来捕获这样的异常,让方法直接抛出

[java] view plain copy
  1. void mySubTask() throws InterruptedException {     
  2.     ...     
  3.     sleep(delay);     
  4.     ...     
  5. }   

第三个方法,设置中断信号量。

中断线程最好的,最受推荐的方式是,使用共享变量(shared variable)发出信号,告诉线程必须停止正在运行的任务。线程必须周期性的核查这一变量,然后有秩序地中止任务。

[java] view plain copy
  1. package com.ljq.test;  
  2.   
  3. public class ThreadTest extends Thread{  
  4.     //线程中断信号量  
  5.     volatile boolean stop=false;   
  6.       
  7.     public static void main(String[] args) throws Exception {  
  8.         ThreadTest thread=new ThreadTest();  
  9.         System.out.println("Starting thread...");     
  10.         thread.start();     
  11.         Thread.sleep(3000);     
  12.         System.out.println("Asking thread to stop...");     
  13.         // 设置中断信号量     
  14.         thread.stop = true;     
  15.         Thread.sleep(3000);     
  16.         System.out.println("Stopping application...");    
  17.     }  
  18.       
  19.       
  20.     @Override  
  21.     public void run() {  
  22.         //每隔一秒检测一下中断信号量  
  23.         while(!stop){  
  24.             System.out.println("Thread is running!");  
  25.             long begin=System.currentTimeMillis();  
  26.             /** 
  27.              * 使用while循环模拟sleep方法,这里不要使用sleep,否则在阻塞时会抛InterruptedException异常而退出循环, 
  28.              * 这样while检测stop条件就不会执行,失去了意义。  
  29.              */  
  30.             while ((System.currentTimeMillis() - begin < 1000)) {  
  31.                   
  32.             }     
  33.         }  
  34.         System.out.println("Thread exiting under request!");     
  35.     }  
  36. }  

注意:这里将stop变量定义为volatile类型,防止由于本地缓存产生错误。

参考文章: 

http://www.cnblogs.com/linjiqin/archive/2011/04/11/2012695.html

http://blog.csdn.net/oscar999/article/details/1755759

原创粉丝点击