java多线程(九)线程中断


    一个线程在未正常结束之前, 被强制终止是很危险的事情. 因为它可能带来完全预料不到的严重后果. 所以你看到Thread.suspend, Thread.stop等方法都被Deprecated了.
那么不能直接把一个线程搞挂掉, 但有时候又有必要让一个线程死掉, 或者让它结束某种等待的状态 该怎么办呢? 优雅的方法就是, 给那个线程一个中断信号, 让它自己决定该怎么办. 比如说, 在某个子线程中为了等待一些特定条件的到来, 你调用了Thread.sleep(10000), 预期线程睡10秒之后自己醒来, 但是如果这个特定条件提前到来的话, 你怎么通知一个在睡觉的线程呢? 又比如说, 主线程通过调用子线程的join方法阻塞自己以等待子线程结束, 但是子线程运行过程中发现自己没办法在短时间内结束, 于是它需要想办法告诉主线程别等我了. 这些情况下, 就需要中断. 
中断是通过调用Thread.interrupt()方法来做的. 这个方法通过修改了被调用线程的中断状态来告知那个线程, 说它被中断了. 对于非阻塞中的线程, 只是改变了中断状态, 即Thread.isInterrupted()将返回true; 对于可取消的阻塞状态中的线程, 比如等待在这些函数上的线程, Thread.sleep(), Object.wait(), Thread.join(), 这个线程收到中断信号后, 会抛出InterruptedException, 同时会把中断状态置回为false.

void interrupt()
  中断线程。(打一个中断标记)
static boolean interrupted()
 测试当前线程是否已经中断。
   
boolean isInterrupted()
    测试线程是否已经中断。
  
  interrupt()方法用于中断线程,通常的理解来看,只要某个线程启动后,调用了该方法,则该线程不能继续执行了,来看个小例子:
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public class InterruptTest {  
  2.     public static void main(String[] args) throws InterruptedException {  
  3.         MyThread t = new MyThread("MyThread");  
  4.         t.start();  
  5.         Thread.sleep(100);// 睡眠100毫秒  
  6.         t.interrupt();// 中断t线程  
  7.     }  
  8. }  
  9. class MyThread extends Thread {  
  10.     int i = 0;  
  11.     public MyThread(String name) {  
  12.         super(name);  
  13.     }  
  14.     public void run() {  
  15.         while(true) {// 死循环,等待被中断  
  16.             System.out.println(getName() + getId() + "执行了" + ++i + "次");  
  17.         }  
  18.     }  
  19. }  
       运行后,我们发现,线程t一直在执行,没有被中断,原来interrupt()是骗人的,汗!其实interrupt()方法并不是中断线程的执行,而是为调用该方法的线程对象打上一个标记,设置其中断状态为true,通过isInterrupted()方法可以得到这个线程状态,我们将上面的程序做一个小改动:
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public class InterruptTest {  
  2.     public static void main(String[] args) throws InterruptedException {  
  3.         MyThread t = new MyThread("MyThread");  
  4.         t.start();  
  5.         Thread.sleep(100);// 睡眠100毫秒  
  6.         t.interrupt();// 中断t线程  
  7.     }  
  8. }  
  9. class MyThread extends Thread {  
  10.     int i = 0;  
  11.     public MyThread(String name) {  
  12.         super(name);  
  13.     }  
  14.     public void run() {  
  15.         while(!isInterrupted()) {// 当前线程没有被中断,则执行  
  16.             System.out.println(getName() + getId() + "执行了" + ++i + "次");  
  17.         }  
  18.     }  
  19. }  

       这样的话,线程被顺利的中断执行了。很多人实现一个线程类时,都会再加一个flag标记,以便控制线程停止执行,其实完全没必要,通过线程自身的中断状态,就可以完美实现该功能。如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException。 我们可以捕获该异常,并且做一些处理。另外,Thread.interrupted()方法是一个静态方法,它是判断当前线程的中断状态,需要注意的是,线程的中断状态会由该方法清除。换句话说,如果连续两次调用该方法,则第二次调用将返回 false(在第一次调用已清除了其中断状态之后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外)。



中断正在sleep的线程


class MyThread extends Thread {

	@Override
	public void run() {
		while (true) {
			System.out.println(Thread.currentThread().getName());
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

public class InterruptTest {

	public static void main(String[] args) {
		MyThread myThread = new MyThread();

		myThread.start();
		try {
			Thread.sleep(2000);
			myThread.interrupt();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

在主线程里启动子线程,子线程sleep 3s,然后主线程sleep 2s,醒来后中断子线程。因为子线程正在sleep,就像你累了在睡觉的时候,被老板打了一拳,醒来后( sleep时间结束)还傻傻的干事。但是 InterruptedException后会清除中断标志。

Thread-0
java.lang.InterruptedException: sleep interrupted
Thread-0
	at java.lang.Thread.sleep(Native Method)
	at MyThread.run(InterruptTest.java:13)
Thread-0
Thread-0
Thread-0
Thread-0
Thread-0

class MyThread extends Thread {

	@Override
	public void run() {
		while (!Thread.currentThread().isInterrupted()) {
			System.out.println(Thread.currentThread().getName()+"isInterrupted="+Thread.currentThread().isInterrupted());
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				e.printStackTrace();
				System.out.println("isInterrupted="+Thread.currentThread().isInterrupted());
				break;
			}
		}
	}
}

public class InterruptTest {

	public static void main(String[] args) {
		MyThread myThread = new MyThread();

		myThread.start();
		try {
			Thread.sleep(2000);
			myThread.interrupt();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}


子线程中断的时候遇到break,退出了循环,接受到中断异常时改变了中断状态

Thread-0isInterrupted=false
java.lang.InterruptedException: sleep interrupted
isInterrupted=false
	at java.lang.Thread.sleep(Native Method)
	at MyThread.run(InterruptTest.java:8)



子线程任务太多,自己中断自己


class MyThread extends Thread {

	@Override
	public void run() {
		
		while (true) {
			System.out.println(Thread.currentThread().getName()+"isInterrupted="+Thread.currentThread().isInterrupted());
			try {
				Thread.sleep(5000);
				//自己任务太多,自己中断自己
				Thread.currentThread().interrupt();
				
			} catch (InterruptedException e) {
				e.printStackTrace();
				System.out.println("isInterrupted="+Thread.currentThread().isInterrupted());
				break;
			}
		}
	}
}

public class InterruptTest {

	public static void main(String[] args) {
		MyThread myThread = new MyThread();

		myThread.start();
	    //主线程等待子线程完成任务
		try {
			myThread.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
			System.out.println("子线程中断");
		}
		System.out.println(Thread.currentThread().getName());
	}
}



Thread-0isInterrupted=false
Thread-0isInterrupted=true
java.lang.InterruptedException: sleep interrupted
isInterrupted=false
main
	at java.lang.Thread.sleep(Native Method)
	at MyThread.run(InterruptTest.java:9)


中断状态可以通过 Thread.isInterrupted()来读取,并且可以通过一个名为 Thread.interrupted()的静态方法读取和清除状态(即调用该方法结束之后, 中断状态会变成false)。

由于处于阻塞状态的线程 被中断后抛出exception并置回中断状态, 有时候是不利的, 因为这个中断状态可能会作为别的线程的判断条件, 所以稳妥的办法是在处理exception的地方把状态复位:

boolean interrupted = false;  
try {  
    while (true) {  
        try {  
            return blockingQueue.take();  
        } catch (InterruptedException e) {  
            interrupted = true;  
        }  
    }  
} finally {  
    if (interrupted){  
        Thread.currentThread().interrupt();  
    }  
} 

当代码调用中须要抛出一个InterruptedException, 你可以选择把中断状态复位, 也可以选择向外抛出InterruptedException, 由外层的调用者来决定.

不是所有的阻塞方法收到中断后都可以取消阻塞状态, 输入和输出流类会阻塞等待 I/O 完成,但是它们不抛出 InterruptedException,而且在被中断的情况下也不会退出阻塞状态. 

尝试获取一个内部锁的操作(进入一个 synchronized 块)是不能被中断的,但是 ReentrantLock 支持可中断的获取模式即 tryLock(long time, TimeUnit unit)。















参考:http://blog.csdn.net/ghsau/article/details/17560467

    http://blog.csdn.net/sunxing007/article/details/9123363

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值