赞
踩
目录
3.1、场景一通过调用thread.interrupt()方法停止线程
3.2、场景二:阻塞状态下也可以通过thread.interrupt()停止线程
俗话说:上山容易下山难。知道如何启动线程,那么到底如何停止线程呢?本文将讲解Java中三种场景下如何正确的停止线程,分别是普通情况、堵塞状态、循环中堵塞状态,三种情况下如何正确的停止线程。
我们只能调用线程的interrupt()方法通知系统停止线程,并不能强制停止线程。线程能否停止,何时停止,取决于系统。
Java中线程的
stop()
、suspend()
、resume()
三个方法都已经被弃用,所以不再使用stop()
方法停止线程。
创建一个子线程,子线程汇总循环打印数字。然后我们在其他线程中,调用子线程的interrupt()
方法停止线程,观察停止前后的控制台输出情况,理解上述线程停止的含义。
- public class StopNormalThread {
-
- public static void main(String[] args) throws InterruptedException {
- // 最好的停止线程方式:通过interrupt通知线程停止线程;而且只能通知,并不能强制让其停止。
- testInterruptThread();
-
- }
-
- /**
- * 线程只能通知停止,不能强制立刻停止测试。
- */
- private static void testInterruptThread() throws InterruptedException {
- Thread thread = new Thread(new Runnable() {
- public void run() {
- for (int i = 0; i <= 1000000; i++) {
- // 判断如果线程没有被中断,则继续输出
- if (!Thread.currentThread().isInterrupted()) {
- System.out.println("当前输出位置:" + i);
- }
- }
- }
- });
- thread.start();
- System.out.println("子线程已经启动");
- //主线程休眠,让子线程跑一会儿,然后让子线程停止
- Thread.sleep(1000);
- System.out.println("主线程休眠结束,开始停止子线程");
- // 终止后,发现for循环还会继续输出内容,少许时间后才停止。说明我们无法控制线程立刻停止。
- thread.interrupt();
- System.out.println("子线程已被停止");
- }
-
- }
程序输出节选:
子线程已经启动
当前输出位置:0
当前输出位置:1
当前输出位置:2
略……
当前输出位置:430290
当前输出位置:430291
当前输出位置:430292
主线程休眠结束,开始停止子线程
当前输出位置:430293
当前输出位置:430294
当前输出位置:430295
当前输出位置:430296
当前输出位置:430297
当前输出位置:430298
当前输出位置:430299
当前输出位置:430300
当前输出位置:430301
当前输出位置:430302
当前输出位置:430303
子线程已被停止Process finished with exit code 0
运行结果解释:
我们可以看出,子线程创建并启动后,开始输出数字,当主线程中调用thread.interrupt()方法时,即通知系统要停止子线程的运行了,此时控制台中还是会有数字继续输出,这就表明:我们只能通过thread.interrupt()方法通知系统停止子线程,但子线程可能不会立即停止,可能还会继续运行一段时间才会停止。
什么是堵塞状态:阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态,即还没有真正运行Synchronized修饰的代码时的状态。BLOCKED或WAITING或TIME_WAITING这三种统称为堵塞状态
代码逻辑描述:
在主线程中创建一个子线程并运行,然后休眠两秒后,让子线程停止。
在子线程中,循环打印数字,然后让子线程休眠1秒(此时子线程进入阻塞状态),然后子线程会被停止。
public class StopBlockThread { public static void main(String[] args) throws InterruptedException { testBlockingInterruptThread(); } /** * 中止堵塞状态的线程示例 */ private static void testBlockingInterruptThread() { try { Thread thread = new Thread(new Runnable() { public void run() { for (int i = 0; i <= 1000000; i++) { // 判断如果线程没有被中断,则继续输出 if (!Thread.currentThread().isInterrupted()) { System.out.println("当前输出位置:" + i); } } try { // 模拟线程堵塞 System.out.println("--1--模拟线程堵塞中"); Thread.sleep(1000); System.out.println("--2--此行不会被打印,即线程以被停止"); } catch (InterruptedException e) { e.printStackTrace(); } } }); thread.start(); System.out.println("让子线程运行两秒,然后再通知其停止"); Thread.sleep(2000); /*让子线程for循环在没有循环结束时,接收到停止信号,此时子线程停止,并执行sleep(模拟阻塞状态)时, 会抛出sleep interrupted中断异常,表示堵塞状态也被中断了,即堵塞状态的线程成功被终止了 */ thread.interrupt(); System.out.println("通知停止线程"); } catch (InterruptedException e) { e.printStackTrace(); } } }程序输出节选:
让子线程运行两秒,然后再通知其停止 当前输出位置:0 当前输出位置:1 当前输出位置:2 略…… 当前输出位置:897419 当前输出位置:897420 当前输出位置:897421 通知停止线程 --1--模拟线程堵塞中 java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at thread.stop.StopBlockThread$1.run(StopBlockThread.java:31) at java.lang.Thread.run(Thread.java:748) Process finished with exit code 0运行结果解释:
分析下子线程的运行流程,子线程启动后循环打印数字,打印语句有一个判断条件!Thread.currentThread().isInterrupted(),意思是只要不被中断才打印,若中断了,就不再打印,转而往下执行Thread.sleep(1000)语句,进入堵塞状态。
此时主线程调用了thread.interrupt(),系统尝试中断子线程,发现子线程在阻塞状态中,所以会抛出异常sleep interrupted,然后我们发现控制台输出Process finished with exit code 0,表示线程被正常停止了。
代码逻辑描述:
主线程中创建一个子线程并运行,两秒钟后,停止子线程。
子线程run()方法中,写一个循环打印数字逻辑,并在每次循环中,都调用一次Thread.sleep(20),目的是让每次循环都进入堵塞状态。此时要想正常停止线程,必须要在循环外部增加try-catch语句,即当阻塞被停止时,会抛出异常,此时即可终止循环,停止线程。
public class StopLoopBlockThread { public static void main(String[] args) { testLoopBlockStopThread(); } /** * 循环中存在堵塞的线程停止示例(关键是将循环放到try-catch内部才生效) */ private static void testLoopBlockStopThread() { Thread thread = new Thread(new Runnable() { public void run() { try { /* 此处不需要判断线程是否已经被中断了,因为如果在循环中的休眠过程中(堵塞时), 收到interrupt信号,则会立刻抛出停止休眠异常*/ for (int i = 0; i < 1000; i++) { System.out.println("当前输出位置:" + i); // 模拟每次循环都堵塞 Thread.sleep(20); } } catch (InterruptedException e) { /* try-catch一定放在循环外部,否则线程将不会停止。因为中断异常在循环中被捕获, 但循环并没有满足循环停止条件,所以知道循环运行结束,才会停止。即时在循环终止条件中, 添加`!Thread.currentThread().isInterrupted()`判断,循环也不会停止,因为线程的sleep()方法, 一旦抛出被中断异常后,其isInterrupted标记也会被清除,所以也无法立即停止线程*/ e.printStackTrace(); } } }); thread.start(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } thread.interrupt(); } }程序输出节选:
当前输出位置:0 当前输出位置:1 当前输出位置:2 当前输出位置:3 略... 当前输出位置:95 当前输出位置:96 java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at thread.stop.StopLoopBlockThread$1.run(StopLoopBlockThread.java:27) at java.lang.Thread.run(Thread.java:748) Process finished with exit code 0
运行结果解释:
本例最关键地方在于如果仅仅把try-catch语句包裹子线程中的在Thread.sleep(20)上,一旦接收到终止信号,程序开始终止sleep()方法的阻塞状态会抛出异常,此时异常将在循环体内被捕获,循环并不能终止,子线程还是会继续运行,一直运行到for循环结束才能正常停止。这样就不符合我们预期,我们想让程序尽快的做出停止操作,如果程序没有终止,则会造成很多不可挽回的结果。
场景三:循环中堵塞状态下正确的停止线程,也可以通过thread.interrupt()停止线程,但需要在子线程的循环外部增加
try-catch
代码块,捕获到中止堵塞状态异常时,也能停止线程。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。