目录
- 线程停止的原理
- 如何正确停止线程
- 在普通情况下停止线程
- 在阻塞情况下停止线程
- 线程在每次迭代后都阻塞
- 停止线程的最佳实践
- 错误停止的方法
- 被弃用的stop,suspend和resume方法
- 用volatile设置boolean标记位
- interrupt源码查看
- interrupt相关函数练习
线程停止的原理
使用interrupt来通知,而不是强制
java提供了interrrupt让一个线程来通知另一个线程停止
如果想中断一个线程,但是那个线程不想去中断,那就无能为力,我们没有强制去中断线程的手段,因为线程停止前需要做一定的收尾工作
所以正确停止线程,是如何用interrupt来通知那个线程,以及被停止的线程如何进行配合
如何正确停止线程
在普通情况下停止线程
代码展示
- 调用interrupt没有作用
- 下面这段代码,执行interrupt之后,线程并没有被中断
- 因为被执行的线程并没有相应中断的方式
| public class stopThreadWithoutSleep implements Runnable{ |
| public static void main(String[] args) throws InterruptedException { |
| Thread thread = new Thread(new stopThreadWithoutSleep()); |
| thread.start(); |
| Thread.sleep(); |
| thread.interrupt(); |
| } |
| @Override |
| public void run() { |
| int num =; |
| while(num <= Integer.MAX_VALUE /) { |
| if (num % == 0) { |
| System.out.println(num + "是的倍数"); |
| } |
| num++; |
| } |
| System.out.println("结束"); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
- 被执行线程加上相应中断的操作之后
- 结果可知,被执行线程相应一秒之后就结束了
| public class stopThreadWithoutSleep implements Runnable{ |
| public static void main(String[] args) throws InterruptedException { |
| Thread thread = new Thread(new stopThreadWithoutSleep()); |
| thread.start(); |
| Thread.sleep(); |
| thread.interrupt(); |
| } |
| @Override |
| public void run() { |
| int num =; |
| while(!Thread.currentThread().isInterrupted() && num <= Integer.MAX_VALUE /) { |
| if (num % == 0) { |
| System.out.println(num + "是的倍数"); |
| } |
| num++; |
| } |
| System.out.println("结束"); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
在阻塞情况下停止线程
代码展示
- 中断之后,抛出异常
- 线程在sleep的过程中,会catch到InterruptedException这个异常,从而相应中断
| public class stopThreadWithSleep { |
| public static void main(String[] args) { |
| Runnable runnable = () -> { |
| int num =; |
| while (num <= && !Thread.currentThread().isInterrupted()) { |
| if (num % == 0) { |
| System.out.println(num + "是的倍数"); |
| } |
| num++; |
| } |
| try { |
| Thread.sleep(); |
| } catch (InterruptedException e) { |
| e.printStackTrace(); |
| } |
| }; |
| Thread thread = new Thread(runnable); |
| thread.start(); |
| try { |
| Thread.sleep(); |
| } catch (InterruptedException e) { |
| e.printStackTrace(); |
| } |
| thread.interrupt(); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
线程在每次迭代后都阻塞
- 代码展示 即使不在while判断是否中断,sleep也能中断异常
| public class stopThreadWithSleepEveryLoop { |
| public static void main(String[] args) { |
| Runnable runnable = () -> { |
| int num =; |
| try { |
| while (num <=) { |
| if (num % == 0) { |
| System.out.println(num + "是的倍数"); |
| } |
| num++; |
| Thread.sleep(); |
| } |
| } catch (InterruptedException e) { |
| e.printStackTrace(); |
| } |
| }; |
| Thread thread = new Thread(runnable); |
| thread.start(); |
| try { |
| Thread.sleep(); |
| } catch ( |
| InterruptedException e) { |
| e.printStackTrace(); |
| } |
| thread.interrupt(); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
当catch写到while内,则不能正常中断
| public class CantInterrupt { |
| public static void main(String[] args) { |
| Runnable runnable = () -> { |
| int num =; |
| while (num <=) { |
| if (num % == 0) { |
| System.out.println(num + "是的倍数"); |
| } |
| num ++; |
| try { |
| Thread.sleep(); |
| } catch (InterruptedException e) { |
| e.printStackTrace(); |
| } |
| } |
| }; |
| Thread thread = new Thread(runnable); |
| thread.start(); |
| try { |
| Thread.sleep(); |
| } catch ( |
| InterruptedException e) { |
| e.printStackTrace(); |
| } |
| thread.interrupt(); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
- 即使在while的判断条件中,加上检测中断的机制,也不能正常中断
- 因为java的sleep函数,一旦相应中断,就会将中断的标志位删除
| public class CantInterrupt { |
| public static void main(String[] args) { |
| Runnable runnable = () -> { |
| int num =; |
| while (num <= && !Thread.currentThread().isInterrupted()) { |
| if (num % == 0) { |
| System.out.println(num + "是的倍数"); |
| } |
| num++; |
| try { |
| Thread.sleep(); |
| } catch (InterruptedException e) { |
| e.printStackTrace(); |
| } |
| } |
| }; |
| Thread thread = new Thread(runnable); |
| thread.start(); |
| try { |
| Thread.sleep(); |
| } catch ( |
| InterruptedException e) { |
| e.printStackTrace(); |
| } |
| thread.interrupt(); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
停止线程的最佳实践
- 在方法签名中抛出异常,在run方法中强制进行try catch
| public class StopThreadInProd implements Runnable{ |
| public static void main(String[] args) throws InterruptedException { |
| Thread thread = new Thread(new StopThreadInProd()); |
| thread.start(); |
| Thread.sleep(); |
| thread.interrupt(); |
| } |
| @Override |
| public void run() { |
| while (true) { |
| System.out.println("start"); |
| try { |
| throwInMethod(); |
| } catch (InterruptedException e) { |
| System.out.println("保存日志/关闭程序"); |
| e.printStackTrace(); |
| } |
| } |
| } |
| private void throwInMethod() throws InterruptedException { |
| Thread.sleep(); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
- 在catch语句中调用Thread.currentThread().interrupt恢复中断状态
- 结果:抛出异常,程序结束
| public class StopThreadInProd implements Runnable{ |
| public static void main(String[] args) throws InterruptedException { |
| Thread thread = new Thread(new StopThreadInProd()); |
| thread.start(); |
| Thread.sleep(); |
| thread.interrupt(); |
| } |
| @Override |
| public void run() { |
| while (true) { |
| if (Thread.currentThread().isInterrupted()) { |
| System.out.println("Interrupt"); |
| break; |
| } |
| reInterrupt(); |
| } |
| } |
| private void reInterrupt() { |
| try { |
| Thread.sleep(); |
| } catch (InterruptedException e) { |
| Thread.currentThread().interrupt(); |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
- 依照上面的代码,如果方法没有没有重新抛出异常
- 结果:程序抛出异常,但是程序没有停止运行
| public class StopThreadInProd implements Runnable{ |
| public static void main(String[] args) throws InterruptedException { |
| Thread thread = new Thread(new StopThreadInProd()); |
| thread.start(); |
| Thread.sleep(); |
| thread.interrupt(); |
| } |
| @Override |
| public void run() { |
| while (true) { |
| if (Thread.currentThread().isInterrupted()) { |
| System.out.println("Interrupt"); |
| break; |
| } |
| reInterrupt(); |
| } |
| } |
| private void reInterrupt() { |
| try { |
| Thread.sleep(); |
| } catch (InterruptedException e) { |
| |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
错误停止的方法
被弃用的stop,suspend和resume方法
- 使用stop停止线程,会导致线程运行一半突然停止,没办法完成最基本的操作,会造成脏数据
- 下面这段代码的结果会造成一个连队只有部分人领取到了装备
- stop是不安全的,会直接停止监视器
- suspend和resume不会破坏对象,但是会让线程挂起,不释放锁,容易造成死锁
| public class StopThread implements Runnable{ |
| @Override |
| public void run() { |
| |
| |
| for (int i =; i < 5; i++) { |
| System.out.println("连队" + i + "领取武器"); |
| for (int j =; j < 10; j++) { |
| System.out.println(j); |
| try { |
| Thread.sleep(); |
| } catch (InterruptedException e) { |
| e.printStackTrace(); |
| } |
| } |
| System.out.println("连队" + i + "领取完毕"); |
| } |
| } |
| public static void main(String[] args) { |
| Thread thread = new Thread(new StopThread()); |
| thread.start(); |
| try { |
| thread.sleep(); |
| } catch (InterruptedException e) { |
| e.printStackTrace(); |
| } |
| thread.stop(); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
用volatile设置boolean标记位
| public class Volatile implements Runnable { |
| private volatile boolean canceled = false; |
| @Override |
| public void run() { |
| int num =; |
| try { |
| while (num <= && !canceled) { |
| if (num % == 0) { |
| System.out.println(num + " 是的倍数"); |
| } |
| num++; |
| Thread.sleep(); |
| } |
| } catch (InterruptedException e) { |
| e.printStackTrace(); |
| } |
| } |
| public static void main(String[] args) throws InterruptedException { |
| Volatile v = new Volatile(); |
| Thread thread = new Thread(v); |
| thread.start(); |
| Thread.sleep(); |
| v.canceled = true; |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
- 当陷入阻塞的时候,是无法停止线程的
- 下面这段代码的运行结果,并没有打印生产者停止运行,说明根本没有执行生产者的finally那部分代码
- 同时程序也没停止
- 原因见生产者代码 while循环中的注释
| |
| public class cantStop { |
| public static void main(String[] args) throws InterruptedException { |
| |
| |
| |
| ArrayBlockingQueue storage = new ArrayBlockingQueue(); |
| Producer producer = new Producer(storage); |
| Thread producerThread = new Thread(producer); |
| producerThread.start(); |
| Thread.sleep(); |
| Consumer consumer = new Consumer(storage); |
| while (consumer.needMore()) { |
| System.out.println(consumer.storage.take() + "被消费"); |
| Thread.sleep(); |
| } |
| System.out.println("消费者不需要更多数据"); |
| |
| producer.canceled = true; |
| } |
| } |
| |
| class Producer implements Runnable { |
| public volatile boolean canceled = false; |
| BlockingQueue storage; |
| public Producer(BlockingQueue storage) { |
| this.storage = storage; |
| } |
| @Override |
| public void run() { |
| int num =; |
| try { |
| while (num <= && !canceled) { |
| if (num % == 0) { |
| |
| storage.put(num); |
| System.out.println("num" + "生产"); |
| } |
| num++; |
| } |
| } catch (InterruptedException e) { |
| e.printStackTrace(); |
| } finally { |
| System.out.println("生产者停止运行"); |
| } |
| } |
| } |
| |
| class Consumer { |
| BlockingQueue storage; |
| public Consumer(BlockingQueue storage) { |
| this.storage = storage; |
| } |
| public boolean needMore() { |
| if (Math.random() >.9) { |
| return false; |
| } |
| return true; |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
- 将上面代码用interrupt进行中断
- 程序成功停止
| public class finxed { |
| public static void main(String[] args) throws InterruptedException { |
| finxed finxed = new finxed(); |
| |
| |
| |
| ArrayBlockingQueue storage = new ArrayBlockingQueue(); |
| Producer producer = finxed.new Producer(storage); |
| Thread producerThread = new Thread(producer); |
| producerThread.start(); |
| Thread.sleep(); |
| Consumer consumer = finxed.new Consumer(storage); |
| while (consumer.needMore()) { |
| System.out.println(consumer.storage.take() + "被消费"); |
| Thread.sleep(); |
| } |
| System.out.println("消费者不需要更多数据"); |
| |
| producerThread.interrupt(); |
| } |
| class Producer implements Runnable { |
| public volatile boolean canceled = false; |
| BlockingQueue storage; |
| public Producer(BlockingQueue storage) { |
| this.storage = storage; |
| } |
| @Override |
| public void run() { |
| int num =; |
| try { |
| while (num <= && !Thread.currentThread().isInterrupted()) { |
| if (num % == 0) { |
| storage.put(num); |
| System.out.println("num" + "生产"); |
| } |
| num++; |
| } |
| } catch (InterruptedException e) { |
| e.printStackTrace(); |
| } finally { |
| System.out.println("生产者停止运行"); |
| } |
| } |
| } |
| class Consumer { |
| BlockingQueue storage; |
| public Consumer(BlockingQueue storage) { |
| this.storage = storage; |
| } |
| public boolean needMore() { |
| if (Math.random() >.9) { |
| return false; |
| } |
| return true; |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
interrupt源码查看
- 这段代码做的都是一些判断,真正执行中断的代码时interrupt0
- interrupt0是native代码
| public void interrupt() { |
| if (this != Thread.currentThread()) |
| checkAccess(); |
| synchronized (blockerLock) { |
| Interruptible b = blocker; |
| if (b != null) { |
| interrupt(); |
| b.interrupt(this); |
| return; |
| } |
| } |
| interrupt(); |
| } |
| private native void interrupt(); |
interrupt相关函数练习
- isInterrupted获取中断标志,获取的是前面的线程
- interrupted获取中断标志并重置,只关心执行的线程,所以下面代码执行的是main线程
| public class InterruptedTest { |
| public static void main(String[] args) throws InterruptedException { |
| Thread thread = new Thread(new Runnable() { |
| @Override |
| public void run() { |
| while (true) { |
| |
| } |
| } |
| }); |
| thread.start(); |
| thread.interrupt(); |
| |
| System.out.println(thread.isInterrupted()); |
| |
| System.out.println(thread.interrupted()); |
| System.out.println(Thread.interrupted()); |
| System.out.println(thread.isInterrupted()); |
| thread.join(); |
| System.out.println("over"); |
| } |
| } |