线程结束 2023-02-15 22:44 知道了如何开启一个线程,那如何结束一个线程呢? - 1、线程执行完,自然结束。 - 2、设置为后台线程,随主线程结束。 - 3、使用stop()强制结束,不安全。 - 4、使用volatile+共享变量,完成子线程的通信,然后退出线程。 - 5、使用jdk推荐的interrupt(),打断线程,该方式可以处理不同的情况。 #### 1、线程代码执行完了,自然结束。 ```java package com.example.demo.ThreadPool; import lombok.extern.slf4j.Slf4j; /** * @Author:Hanxu * @url:https://riun.xyz/ * @Date:2023/2/15 21:18 * 线程结束的方式1:自然结束 */ @Slf4j public class Demo2 { public static void main(String[] args) { new Thread(() -> { log.info("{}线程执行完了", Thread.currentThread().getName()); }).start(); log.info("{}线程执行完了", Thread.currentThread().getName()); } } ``` 上述代码输出: ``` 21:20:00.065 [Thread-0] INFO com.example.demo.ThreadPool.Demo2 - Thread-0线程执行完了 21:20:00.065 [main] INFO com.example.demo.ThreadPool.Demo2 - main线程执行完了 Process finished with exit code 0 //输出后程序结束 ``` 这种情况遇到子线程一直在执行的时候就无法结束了,例如: ```java package com.example.demo.ThreadPool.ThreadDeath; import lombok.extern.slf4j.Slf4j; /** * @Author:Hanxu * @url:https://riun.xyz/ * @Date:2023/2/15 21:27 */ @Slf4j public class Demo1_1 { public static void main(String[] args) { new Thread(() -> { while (true) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } log.info("{}线程正在执行", Thread.currentThread().getName()); } }).start(); log.info("{}线程执行完了", Thread.currentThread().getName()); } } ``` 上述代码输出: ``` 21:27:40.950 [main] INFO com.example.demo.ThreadPool.ThreadDeath.Demo1_1 - main线程执行完了 21:27:41.464 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.Demo1_1 - Thread-0线程正在执行 21:27:41.968 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.Demo1_1 - Thread-0线程正在执行 21:27:42.479 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.Demo1_1 - Thread-0线程正在执行 21:27:42.993 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.Demo1_1 - Thread-0线程正在执行 21:27:43.506 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.Demo1_1 - Thread-0线程正在执行 21:27:44.008 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.Demo1_1 - Thread-0线程正在执行 21:27:44.511 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.Demo1_1 - Thread-0线程正在执行 21:27:45.012 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.Demo1_1 - Thread-0线程正在执行 21:27:45.527 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.Demo1_1 - Thread-0线程正在执行 ``` 如果不主动关闭进程,则一直运行下去。原因是该子线程是一个用户线程,它的任务一直没有做完,所以就会一直运行。 #### 2、设置后台线程,随主线程结束 针对上述情况,如果想要随主线程结束时,可以设置为守护线程。 ```java package com.example.demo.ThreadPool.ThreadDeath; import lombok.extern.slf4j.Slf4j; /** * @Author:Hanxu * @url:https://riun.xyz/ * @Date:2023/2/15 21:18 * 线程结束的方式2:守护线程 */ @Slf4j public class Demo2 { public static void main(String[] args) { Thread thread = new Thread(() -> { while (true) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } log.info("{}线程正在执行", Thread.currentThread().getName()); } }); thread.setDaemon(true); thread.start(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } log.info("{}线程执行完了", Thread.currentThread().getName()); } } ``` 上述代码输出: ``` 21:30:41.133 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.Demo2 - Thread-0线程正在执行 21:30:41.638 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.Demo2 - Thread-0线程正在执行 21:30:42.151 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.Demo2 - Thread-0线程正在执行 21:30:42.620 [main] INFO com.example.demo.ThreadPool.ThreadDeath.Demo2 - main线程执行完了 Process finished with exit code 0 ``` #### 3、使用stop()强制结束线程 ```java package com.example.demo.ThreadPool.ThreadDeath; import lombok.extern.slf4j.Slf4j; /** * @Author:Hanxu * @url:https://riun.xyz/ * @Date:2023/2/15 21:32 * 线程结束的方式3:使用stop方法强制结束线程 */ @Slf4j public class Demo3 { public static void main(String[] args) { Thread thread = new Thread(() -> { while (true) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } log.info("{}线程正在执行", Thread.currentThread().getName()); } }); thread.start(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } thread.stop(); log.info("{}线程执行完了", Thread.currentThread().getName()); } } ``` 上述代码输出: ``` 21:39:18.939 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.Demo3 - Thread-0线程正在执行 21:39:19.457 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.Demo3 - Thread-0线程正在执行 21:39:19.966 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.Demo3 - Thread-0线程正在执行 21:39:20.433 [main] INFO com.example.demo.ThreadPool.ThreadDeath.Demo3 - main线程执行完了 Process finished with exit code 0 ``` stop()是已弃用的方法,因为它在高并发时可能造成资源的损坏,破坏一致性。官方对它不安全的解释是:该方法立即停止线程,停止一个线程会导致解锁该条线程持有的锁,其他线程可以“以不一致的状态查看这些对象”。 #### 4、使用volatile共享内存思想,停止线程 ```java package com.example.demo.ThreadPool.ThreadDeath; import lombok.extern.slf4j.Slf4j; /** * @Author:Hanxu * @url:https://riun.xyz/ * @Date:2023/2/15 21:42 * 线程结束的方式4:使用volatile共享内存思想,停止线程 */ @Slf4j public class Demo4 { public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } myThread.stopThread(); log.info("{}线程执行完了", Thread.currentThread().getName()); } } @Slf4j class MyThread extends Thread { private volatile boolean stopFlag = false; public void stopThread() { stopFlag = true; } @Override public void run() { while (true) { if (stopFlag) { break; } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } log.info("{}线程正在执行", Thread.currentThread().getName()); } } } ``` 上述代码输出: ``` 21:45:11.003 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread - Thread-0线程正在执行 21:45:11.506 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread - Thread-0线程正在执行 21:45:12.020 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread - Thread-0线程正在执行 21:45:12.503 [main] INFO com.example.demo.ThreadPool.ThreadDeath.Demo4 - main线程执行完了 21:45:12.535 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread - Thread-0线程正在执行 Process finished with exit code 0 ``` 该方法思想是使用一个共享变量,主线程可以修改这个变量的值,当子线程监视到该变量的值变化时,退出,自然结束。 但是该方法也有缺陷,那就是当子线程处于非运行状态时无法停止线程。非运行状态:sleep()时、wait()时、IO阻塞时。 ##### volatile无法停止的情况 ```java package com.example.demo.ThreadPool.ThreadDeath; import lombok.extern.slf4j.Slf4j; /** * @Author:Hanxu * @url:https://riun.xyz/ * @Date:2023/2/15 22:19 * volatile无法停止的情况:线程处于非运行状态 */ @Slf4j public class Demo4_1 { public static void main(String[] args) { Object monitor = new Object(); MyThread_1 myThread_1 = new MyThread_1(monitor); myThread_1.start(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } myThread_1.stopThread(); log.info("{}线程执行完了", Thread.currentThread().getName()); } } @Slf4j class MyThread_1 extends Thread { private Object monitor; private volatile boolean stopFlag = false; public MyThread_1(Object monitor) { this.monitor = monitor; } public void stopThread() { stopFlag = true; } @Override public void run() { int i = 0; while (true) { synchronized (monitor) { if (stopFlag) { break; } if (i == 5) { try { monitor.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } i++; log.info("{}线程正在执行", Thread.currentThread().getName()); } } } } ``` 上述代码输出:一直卡在这,因为子线程在wait(),不能及时得到stopFlag的状态 ``` 22:23:57.119 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread_1 - Thread-0线程正在执行 22:23:57.122 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread_1 - Thread-0线程正在执行 22:23:57.122 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread_1 - Thread-0线程正在执行 22:23:57.122 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread_1 - Thread-0线程正在执行 22:23:57.122 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread_1 - Thread-0线程正在执行 22:23:59.125 [main] INFO com.example.demo.ThreadPool.ThreadDeath.Demo4_1 - main线程执行完了 ``` #### 5、使用jdk官方推荐的interrupt方法打断线程 官方推荐的interrupt()是stop()的替代品: ```java package com.example.demo.ThreadPool.ThreadDeath; import lombok.extern.slf4j.Slf4j; /** * @Author:Hanxu * @url:https://riun.xyz/ * @Date:2023/2/15 21:50 * 线程结束的方式5:使用jdk官方推荐的interrupt方法打断线程 */ @Slf4j public class Demo5 { public static void main(String[] args) { MyThread2 myThread2 = new MyThread2(); myThread2.start(); try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } myThread2.interrupt(); log.info("{}线程执行完了", Thread.currentThread().getName()); } } @Slf4j class MyThread2 extends Thread { @Override public void run() { while (true) { if (Thread.currentThread().isInterrupted()) { log.info("子线程被打断"); return; } log.info("{}线程正在执行", Thread.currentThread().getName()); } } } ``` 上述代码输出: ``` 22:05:55.761 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread2 - Thread-0线程正在执行 22:05:55.764 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread2 - Thread-0线程正在执行 22:05:55.764 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread2 - Thread-0线程正在执行 22:05:55.764 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread2 - Thread-0线程正在执行 22:05:55.764 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread2 - Thread-0线程正在执行 22:05:55.764 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread2 - Thread-0线程正在执行 22:05:55.764 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread2 - Thread-0线程正在执行 22:05:55.764 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread2 - Thread-0线程正在执行 22:05:55.764 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread2 - Thread-0线程正在执行 22:05:55.764 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread2 - Thread-0线程正在执行 22:05:55.764 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread2 - Thread-0线程正在执行 22:05:55.764 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread2 - Thread-0线程正在执行 22:05:55.764 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread2 - Thread-0线程正在执行 22:05:55.764 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread2 - Thread-0线程正在执行 22:05:55.764 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread2 - Thread-0线程正在执行 22:05:55.764 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread2 - Thread-0线程正在执行 22:05:55.764 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread2 - Thread-0线程正在执行 22:05:55.764 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread2 - Thread-0线程正在执行 22:05:55.764 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread2 - Thread-0线程正在执行 22:05:55.764 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread2 - Thread-0线程正在执行 22:05:55.764 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread2 - Thread-0线程正在执行 22:05:55.765 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread2 - Thread-0线程正在执行 22:05:55.765 [main] INFO com.example.demo.ThreadPool.ThreadDeath.Demo5 - main线程执行完了 22:05:55.765 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread2 - 子线程被打断 Process finished with exit code 0 ``` 使用此方式时需要注意子线程的执行代码里不能有睡眠什么的,如果有的话,打断时可能会抛出异常,异常会清除打断标识,所以就不会return出去: ##### 异常情况,睡眠能吃掉中断标志: ```java package com.example.demo.ThreadPool.ThreadDeath; import lombok.extern.slf4j.Slf4j; /** * @Author:Hanxu * @url:https://riun.xyz/ * @Date:2023/2/15 22:07 * 无法打断的异常情况 */ @Slf4j public class Demo5_1 { public static void main(String[] args) { MyThread3 myThread3 = new MyThread3(); myThread3.start(); try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } myThread3.interrupt(); log.info("{}线程执行完了", Thread.currentThread().getName()); } } @Slf4j class MyThread3 extends Thread { @Override public void run() { while (true) { if (Thread.currentThread().isInterrupted()) { log.info("子线程被打断"); return; } // 子线程这里有睡眠 try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); log.info("我输出了吗?"); } log.info("{}线程正在执行", Thread.currentThread().getName()); } } } ``` 上述代码输出:并且还会一直执行下去 ``` java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at com.example.demo.ThreadPool.ThreadDeath.MyThread3.run(Demo5_1.java:38) 22:10:19.397 [main] INFO com.example.demo.ThreadPool.ThreadDeath.Demo5_1 - main线程执行完了 22:10:19.397 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread3 - 我输出了吗? 22:10:19.399 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread3 - Thread-0线程正在执行 22:10:19.908 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread3 - Thread-0线程正在执行 22:10:20.408 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread3 - Thread-0线程正在执行 22:10:20.922 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread3 - Thread-0线程正在执行 22:10:21.436 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread3 - Thread-0线程正在执行 22:10:21.939 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread3 - Thread-0线程正在执行 22:10:22.443 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread3 - Thread-0线程正在执行 22:10:22.955 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread3 - Thread-0线程正在执行 22:10:23.458 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread3 - Thread-0线程正在执行 22:10:23.971 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread3 - Thread-0线程正在执行 22:10:24.471 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread3 - Thread-0线程正在执行 ``` 因为子线程在执行时,执行睡眠过程有500ms之久,那么当其他线程打断它的时候,很大概率来讲子线程在睡眠。也就是在执行`Thread.sleep(500);`期间被打断,睡眠过程被打断会抛出异常,刚好被catch拦截到,触发**InterruptedException**异常时,JVM会把中断标志位清除掉,所以该循环就会一直进行下去。 > http://www.zlprogram.com/post/5749.html 解决办法就是在catch时重新设置打断标志:Thread.currentThread().interrupt(); ##### 异常情况的正确处理方法 ```java package com.example.demo.ThreadPool.ThreadDeath; import lombok.extern.slf4j.Slf4j; /** * @Author:Hanxu * @url:https://riun.xyz/ * @Date:2023/2/15 22:07 * 无法打断的异常情况 及解决办法 */ @Slf4j public class Demo5_1 { public static void main(String[] args) { MyThread3 myThread3 = new MyThread3(); myThread3.start(); try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } myThread3.interrupt(); log.info("{}线程执行完了", Thread.currentThread().getName()); } } @Slf4j class MyThread3 extends Thread { @Override public void run() { while (true) { if (Thread.currentThread().isInterrupted()) { log.info("子线程被打断"); return; } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); log.info("我输出了吗?"); // 解决办法:重新设置打断标志 Thread.currentThread().interrupt(); } log.info("{}线程正在执行", Thread.currentThread().getName()); } } } ``` 上述代码输出: ``` java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at com.example.demo.ThreadPool.ThreadDeath.MyThread3.run(Demo5_1.java:38) 22:15:50.340 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread3 - 我输出了吗? 22:15:50.340 [main] INFO com.example.demo.ThreadPool.ThreadDeath.Demo5_1 - main线程执行完了 22:15:50.342 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread3 - Thread-0线程正在执行 22:15:50.342 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread3 - 子线程被打断 Process finished with exit code 0 ``` ##### interrupt()能中止非运行状态的线程 ```java package com.example.demo.ThreadPool.ThreadDeath; import lombok.extern.slf4j.Slf4j; /** * @Author:Hanxu * @url:https://riun.xyz/ * @Date:2023/2/15 22:30 * interrupt()能中止非运行状态的线程 */ @Slf4j public class Demo5_2 { public static void main(String[] args) { Object monitor = new Object(); MyThread4 myThread4 = new MyThread4(monitor); myThread4.start(); try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } myThread4.interrupt(); log.info("{}线程执行完了", Thread.currentThread().getName()); } } @Slf4j class MyThread4 extends Thread { private Object monitor; public MyThread4(Object monitor) { this.monitor = monitor; } @Override public void run() { int i = 0; while (true) { synchronized (monitor) { if (Thread.currentThread().isInterrupted()) { log.info("子线程被打断"); return; } if (i == 5) { try { monitor.wait(); } catch (InterruptedException e) { e.printStackTrace(); System.out.println("我执行了吗?"); Thread.currentThread().interrupt(); } } i++; // 打断后这里会多执行一次,实际编码中要注意位置和额外判断 log.info("{}线程正在执行", Thread.currentThread().getName()); } } } } ``` 上述代码输出: ``` 22:36:47.096 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread4 - Thread-0线程正在执行 22:36:47.098 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread4 - Thread-0线程正在执行 22:36:47.098 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread4 - Thread-0线程正在执行 22:36:47.098 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread4 - Thread-0线程正在执行 22:36:47.098 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread4 - Thread-0线程正在执行 22:36:47.099 [main] INFO com.example.demo.ThreadPool.ThreadDeath.Demo5_2 - main线程执行完了 我执行了吗? 22:36:47.099 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread4 - Thread-0线程正在执行 22:36:47.099 [Thread-0] INFO com.example.demo.ThreadPool.ThreadDeath.MyThread4 - 子线程被打断 java.lang.InterruptedException at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java:502) at com.example.demo.ThreadPool.ThreadDeath.MyThread4.run(Demo5_2.java:47) Process finished with exit code 0 ``` --END--
发表评论