有时候我们会需要将正在执行的线程进行打断,可能我们会想到使用Thread类的interrupt()方法去打断这个线程,interrupt()能达到我们的要求吗,试一下。
例1:
System.out.println("主线程开始."); TaskD taskD = new TaskD(); try { taskD.start(); Thread.sleep(1000); /* * 虽然taskD进行interrupt,但是并没有真正中断任务执行 * 只是在taskD的run方法sleep时抛出了InterruptedException */ taskD.interrupt(); System.out.println("taskD interrupt..."); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("主线程结束.");
线程ThreadD的run方法是使用循环以及sheep来模拟耗时的操作,
可以看到在执行了interrupt操作之后,taskD被打断,sleep抛出了InterruptedException异常。
这种方式达到了我们的目的。
例2:
System.out.println("主线程开始."); TaskF taskF = new TaskF(); try { taskF.start(); Thread.sleep(1000); taskF.interrupt(); /* * 虽然taskF进行interrupt,但是并没有真正中断任务执行 * 业务处理正常执行,没有抛出异常。 */ System.out.println("taskF interrupt..."); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("主线程结束.");
线程ThreadF的run方法是使用大数量级的循环来模拟耗时的操作,不使用sheep
可以看到在执行了interrupt操作之后,taskF没有被打断,没有达到我们的目的。
为什么呢,看下api的解释:
If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long, int), sleep(long), or sleep(long, int), methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException.
如果线程在调用 Object 类的 wait()、 wait(long) 或 wait(long, int) 方法,或者该类的 join()、 join(long)、 join(long, int)、 sleep(long) 或 sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException。
也就是说,interrupt只能使通过join、wait、sleep等阻塞的线程执行进行中断。
Thread.interrupt()方法不会中断一个正在运行的线程。这一方法实际上完成的是,在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞的状态。更确切的说,如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,那么,它将接收到一个中断异常(InterruptedException),从而提早地终结被阻塞状态。
那么正确的方式应该是什么呢?使用interrupt加volatile信号量
例3:
public class TaskG extends Thread { /** * 标记变量可见性,从主存中读取变量最新值 */ private volatile boolean stop = true; public void setStop(boolean stop) { this.stop = stop; } @Override public void run() { System.out.println("TaskG.class start..."); for (int i = 0; i < 500000000; i++) { //模拟耗时操作 if (i % 10000000 == 0 && stop) { System.out.println("TaskF loop " + i / 10000000); } } System.out.println("TaskG.class end..."); } }
System.out.println("主线程开始."); TaskG taskG = new TaskG(); try { taskG.start(); Thread.sleep(1000); /* * 通过信号量标记线程任务终止 */ taskG.setStop(false); /* * 在进行打断操作 * 如果taskG没有被阻塞,那么该语句不起作用 * 如果taskG被阻塞,则会被打断,抛出InterruptedException,可以捕获该异常并进行相应的处理 */ taskG.interrupt(); System.out.println("taskG stop..."); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("主线程结束.");
如果线程正在阻塞,那么修改信号量标记时无法起到作用的,所以,所以,所以
正确的终止线程的方式,因该是使用volatile类型的信号量来标记线程的终止状态,并使用interrupt来防止线程正在阻塞。
tips:volatile修饰变量来标记变量的可见性,使当前线程每次都从主存中获取最新值。
源代码: https://git.oschina.net/xujiwei/thread-impl