当前位置:   article > 正文

Java如何正确停止线程_java thread停止

java thread停止

目录

1、简介

2、普通场景下如何停止线程

2.1、如何停止线程:

2.2、注意:

3、代码演示

3.1、场景一通过调用thread.interrupt()方法停止线程

3.2、场景二:阻塞状态下也可以通过thread.interrupt()停止线程

3.3、场景三:循环中堵塞状态下如何停止线程


1、简介

        俗话说:上山容易下山难。知道如何启动线程,那么到底如何停止线程呢?本文将讲解Java中三种场景下如何正确的停止线程,分别是普通情况、堵塞状态、循环中堵塞状态,三种情况下如何正确的停止线程。


2、普通场景下如何停止线程

2.1、如何停止线程:

        我们只能调用线程的interrupt()方法通知系统停止线程,并不能强制停止线程。线程能否停止,何时停止,取决于系统。

2.2、注意:

        Java中线程的stop()suspend()resume()三个方法都已经被弃用,所以不再使用stop()方法停止线程。 

3、代码演示

3.1、场景一通过调用thread.interrupt()方法停止线程

        创建一个子线程,子线程汇总循环打印数字。然后我们在其他线程中,调用子线程的interrupt()方法停止线程,观察停止前后的控制台输出情况,理解上述线程停止的含义。

  1. public class StopNormalThread {
  2. public static void main(String[] args) throws InterruptedException {
  3. // 最好的停止线程方式:通过interrupt通知线程停止线程;而且只能通知,并不能强制让其停止。
  4. testInterruptThread();
  5. }
  6. /**
  7. * 线程只能通知停止,不能强制立刻停止测试。
  8. */
  9. private static void testInterruptThread() throws InterruptedException {
  10. Thread thread = new Thread(new Runnable() {
  11. public void run() {
  12. for (int i = 0; i <= 1000000; i++) {
  13. // 判断如果线程没有被中断,则继续输出
  14. if (!Thread.currentThread().isInterrupted()) {
  15. System.out.println("当前输出位置:" + i);
  16. }
  17. }
  18. }
  19. });
  20. thread.start();
  21. System.out.println("子线程已经启动");
  22. //主线程休眠,让子线程跑一会儿,然后让子线程停止
  23. Thread.sleep(1000);
  24. System.out.println("主线程休眠结束,开始停止子线程");
  25. // 终止后,发现for循环还会继续输出内容,少许时间后才停止。说明我们无法控制线程立刻停止。
  26. thread.interrupt();
  27. System.out.println("子线程已被停止");
  28. }
  29. }

程序输出节选:

子线程已经启动
当前输出位置: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()方法通知系统停止子线程,但子线程可能不会立即停止,可能还会继续运行一段时间才会停止

 

3.2、场景二:阻塞状态下也可以通过thread.interrupt()停止线程

        什么是堵塞状态:阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态,即还没有真正运行Synchronized修饰的代码时的状态。BLOCKED或WAITING或TIME_WAITING这三种统称为堵塞状态


代码逻辑描述:

        在主线程中创建一个子线程并运行,然后休眠两秒后,让子线程停止。

        在子线程中,循环打印数字,然后让子线程休眠1秒(此时子线程进入阻塞状态),然后子线程会被停止。

  1. public class StopBlockThread {
  2. public static void main(String[] args) throws InterruptedException {
  3. testBlockingInterruptThread();
  4. }
  5. /**
  6. * 中止堵塞状态的线程示例
  7. */
  8. private static void testBlockingInterruptThread() {
  9. try {
  10. Thread thread = new Thread(new Runnable() {
  11. public void run() {
  12. for (int i = 0; i <= 1000000; i++) {
  13. // 判断如果线程没有被中断,则继续输出
  14. if (!Thread.currentThread().isInterrupted()) {
  15. System.out.println("当前输出位置:" + i);
  16. }
  17. }
  18. try {
  19. // 模拟线程堵塞
  20. System.out.println("--1--模拟线程堵塞中");
  21. Thread.sleep(1000);
  22. System.out.println("--2--此行不会被打印,即线程以被停止");
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. }
  26. }
  27. });
  28. thread.start();
  29. System.out.println("让子线程运行两秒,然后再通知其停止");
  30. Thread.sleep(2000);
  31. /*让子线程for循环在没有循环结束时,接收到停止信号,此时子线程停止,并执行sleep(模拟阻塞状态)时,
  32. 会抛出sleep interrupted中断异常,表示堵塞状态也被中断了,即堵塞状态的线程成功被终止了 */
  33. thread.interrupt();
  34. System.out.println("通知停止线程");
  35. } catch (InterruptedException e) {
  36. e.printStackTrace();
  37. }
  38. }
  39. }

程序输出节选:

  1. 让子线程运行两秒,然后再通知其停止
  2. 当前输出位置:0
  3. 当前输出位置:1
  4. 当前输出位置:2
  5. 略……
  6. 当前输出位置:897419
  7. 当前输出位置:897420
  8. 当前输出位置:897421
  9. 通知停止线程
  10. --1--模拟线程堵塞中
  11. java.lang.InterruptedException: sleep interrupted
  12. at java.lang.Thread.sleep(Native Method)
  13. at thread.stop.StopBlockThread$1.run(StopBlockThread.java:31)
  14. at java.lang.Thread.run(Thread.java:748)
  15. Process finished with exit code 0

运行结果解释:

        分析下子线程的运行流程,子线程启动后循环打印数字,打印语句有一个判断条件!Thread.currentThread().isInterrupted(),意思是只要不被中断才打印,若中断了,就不再打印,转而往下执行Thread.sleep(1000)语句,进入堵塞状态。

        此时主线程调用了thread.interrupt(),系统尝试中断子线程,发现子线程在阻塞状态中,所以会抛出异常sleep interrupted,然后我们发现控制台输出Process finished with exit code 0,表示线程被正常停止了。


3.3、场景三:循环中堵塞状态下如何停止线程

代码逻辑描述:

        主线程中创建一个子线程并运行,两秒钟后,停止子线程。

        子线程run()方法中,写一个循环打印数字逻辑,并在每次循环中,都调用一次Thread.sleep(20),目的是让每次循环都进入堵塞状态。此时要想正常停止线程,必须要在循环外部增加try-catch语句,即当阻塞被停止时,会抛出异常,此时即可终止循环,停止线程。

  1. public class StopLoopBlockThread {
  2. public static void main(String[] args) {
  3. testLoopBlockStopThread();
  4. }
  5. /**
  6. * 循环中存在堵塞的线程停止示例(关键是将循环放到try-catch内部才生效)
  7. */
  8. private static void testLoopBlockStopThread() {
  9. Thread thread = new Thread(new Runnable() {
  10. public void run() {
  11. try {
  12. /* 此处不需要判断线程是否已经被中断了,因为如果在循环中的休眠过程中(堵塞时),
  13. 收到interrupt信号,则会立刻抛出停止休眠异常*/
  14. for (int i = 0; i < 1000; i++) {
  15. System.out.println("当前输出位置:" + i);
  16. // 模拟每次循环都堵塞
  17. Thread.sleep(20);
  18. }
  19. } catch (InterruptedException e) {
  20. /* try-catch一定放在循环外部,否则线程将不会停止。因为中断异常在循环中被捕获,
  21. 但循环并没有满足循环停止条件,所以知道循环运行结束,才会停止。即时在循环终止条件中,
  22. 添加`!Thread.currentThread().isInterrupted()`判断,循环也不会停止,因为线程的sleep()方法,
  23. 一旦抛出被中断异常后,其isInterrupted标记也会被清除,所以也无法立即停止线程*/
  24. e.printStackTrace();
  25. }
  26. }
  27. });
  28. thread.start();
  29. try {
  30. Thread.sleep(2000);
  31. } catch (InterruptedException e) {
  32. e.printStackTrace();
  33. }
  34. thread.interrupt();
  35. }
  36. }

程序输出节选:

  1. 当前输出位置:0
  2. 当前输出位置:1
  3. 当前输出位置:2
  4. 当前输出位置:3
  5. 略...
  6. 当前输出位置:95
  7. 当前输出位置:96
  8. java.lang.InterruptedException: sleep interrupted
  9. at java.lang.Thread.sleep(Native Method)
  10. at thread.stop.StopLoopBlockThread$1.run(StopLoopBlockThread.java:27)
  11. at java.lang.Thread.run(Thread.java:748)
  12. Process finished with exit code 0

运行结果解释:

        本例最关键地方在于如果仅仅把try-catch语句包裹子线程中的在Thread.sleep(20)上,一旦接收到终止信号,程序开始终止sleep()方法的阻塞状态会抛出异常,此时异常将在循环体内被捕获,循环并不能终止,子线程还是会继续运行,一直运行到for循环结束才能正常停止。这样就不符合我们预期,我们想让程序尽快的做出停止操作,如果程序没有终止,则会造成很多不可挽回的结果。

场景三:循环中堵塞状态下正确的停止线程,也可以通过thread.interrupt()停止线程,但需要在子线程的循环外部增加try-catch代码块,捕获到中止堵塞状态异常时,也能停止线程。

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号