【Java】Java线程中断(Interrupt)与阻塞(park)的区别

周二鸭 2020-11-11 09:44:15
java 线程 中断 interrupt


对于很多刚接触编程的人来说,对于线程中断和线程阻塞两个概念,经常性是混淆起来用,单纯地认为线程中断与线程阻塞的概念是一致的,都是值线程运行状态的停止。其实这个观点是错误的,两者之前有很大的区别,下文就着重介绍两者之间的区别。

线程中断

在一个线程正常结束之前,如果被强制终止,那么就有可能造成一些比较严重的后果,设想一下如果现在有一个线程持有同步锁,然后在没有释放锁资源的情况下被强制休眠,那么这就造成了其他线程无法访问同步代码块。因此我们可以看到在 Java 中类似 Thread#stop() 方法被标为 @Deprecated

针对上述情况,我们不能直接将线程给终止掉,但有时又必须将让线程停止运行某些代码,那么此时我们必须有一种机制让线程知道它该停止了。Java 为我们提供了一个比较优雅的做法,即可以通过 Thread#interrupt() 给线程该线程一个标志位,让该线程自己决定该怎么办。

接下来就用代码来延时下 interrupt() 的作用:

public class InterruptDemo {
static class MyThread implements Runnable {
@Override
public void run() {
for (int i= 0; !Thread.currentThread().isInterrupted() && i < 200000; i++) {
System.out.println(Thread.currentThread().getName() + ":i = " + i);
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread myThread = new Thread(new MyThread());
myThread.start();
// 让线程运行一段时间
Thread.sleep(5);
myThread.interrupt();
// 等待 myThread 运行停止
myThread.join();
System.out.println("end");
}
}

以上代码的运行结果如下:

可以看到,当前线程并没有按 for 循环中的结束量 20000 去跑,而是在被中断后,停止了当前了 for 循环。所以我们可以利用 interrupt 配置线程使用,使得线程在一定的位置停止下来。

不过到这里可能会让人产生一些疑惑,因为在这里看起来,当前线程像是被阻塞掉了,其实并不是的,我们可以利用下面这段代码来演示下:

public class InterruptDemo {
static class MyThread implements Runnable {
@Override
public void run() {
for (int i= 0; i < 200000; i++) {
System.out.println(Thread.currentThread().getName() + ":i = " + i);
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread myThread = new Thread(new MyThread());
myThread.start();
// 让线程运行一段时间
Thread.sleep(5);
myThread.interrupt();
// 等待 myThread 运行停止
myThread.join();
System.out.println("end");
}
}

上面这段代码的运行结果如下:

可见,线程一直打印到 20000,执行完毕后推出线程,并没有像我们预料中在某处中断。所以我们可以得出结论:单纯用 interrupt() 中断线程方法并不能停止当前正在运行的线程,需要配合其他方法才能正确停止线程。

了解完中断的基本概念后,线程的中断还有需要其他需要注意的点:

  • 设置线程中断后,线程内调用 wait()join()slepp() 方法中的一种,都会抛出 InterruptedException 异常,且中断标志位被清除,重新设置为 false;
  • 当线程被阻塞,比如调用了上述三个方法之一,那么此时调用它的 interrupt() 方法,也会产生一个 InterruptedException 异常。因为没有占有 CPU 的线程是无法给自己设置中断状态位置的;
  • 尝试获取一个内部锁的操作(进入一个 synchronized 块)是不能被中断的,但是 ReentrantLock 支持可中断的获取模式:tryLock(long time, TimeUnit unit)
  • 当代码调用中需要抛出一个 InterruptedException,捕获之后,要么继续往上抛,要么重置中断状态,这是最安全的做法。

线程阻塞

上面讲完了线程中断,它其实只是一个标志位,并不能让线程真正的停止下来,那么接下来就来介绍如何真正让线程停止下来。

对于这个问题,Java 中提供了一个较为底层的并发工具类:LockSupport,该类中的核心方法有两个:park(Object blocker) 以及 unpark(Thread thred),前者表示阻塞指定线程,后者表示唤醒指定的线程。

// java.util.concurrent.locks.LockSupport
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}

该方法在 Java 的语言层面上比较简单,最终也是去调用 UNSAFE 中的 native 方法。真正涉及到底层的东西需要去理解 JVM 的源码,这里就不做太多的介绍。不过我们可以用一个简单的例子来演示下这两个方法:

public class LockSupportDemo {
static class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "开始执行");
LockSupport.park();
System.out.println(Thread.currentThread().getName() + "执行结束");
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new MyThread(), "线程:MyThread");
thread.start();
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + "主线程执行中");
LockSupport.unpark(thread);
System.out.println(Thread.currentThread().getName() + "主线程执行结束");
}
}

上述代码的执行结果为:

线程:MyThread开始执行
main主线程执行中
线程:MyThread执行结束
main主线程执行结束

可以看到,myThread 线程在开始执行后停止了下来,等到主线程重新调用 LockSupport.unpark(thread) 后才重新开始执行。

版权声明
本文为[周二鸭]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/jojop/p/13957027.html

  1. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云