interrupt()中断对LockSupport.park()的影响

interrupt()中断对LockSupport.park()的影响park 调用后一定会消耗掉 permit 无论 unpark 操作先做还是后做 如果中断状态为 true 那么 park 无法阻塞 unpark 会使得 permit 为 1 并唤醒处于阻塞的线程 interrupt 会使得中断状态为 true 并调用 unpark sleep wait join 调用后一定会消耗掉中断状态 无论 interrupt 操作先做还是后做

原理简单讲解

首先声明,本文不会去贴native方法的cpp实现,而是以伪代码的形式来理解这些native方法。

  • Thread对象的native实现里有一个成员代表线程的中断状态,我们可以认为它是一个bool型的变量。初始为false。
  • Thread对象的native实现里有一个成员代表线程是否可以阻塞的许可permit,我们可以认为它是一个int型的变量,但它的值只能为0或1。当为1时,再累加也会维持1。初始为0。

调用park()与unpark()

park/unpark实现的伪代码

下面将以伪代码的实现来说明park/unpark的实现。

park() { 
    if(permit > 0) { 
    permit = 0; return; } if(中断状态 == true) { 
    return; } 阻塞当前线程; // 将来会从这里被唤醒 if(permit > 0) { 
    permit = 0; } } 

可见,只要permit为1或者中断状态为true,那么执行park就不能够阻塞线程。park只可能消耗掉permit,但不会去消耗掉中断状态

unpark(Thread thread) { 
    if(permit < 1) { 
    permit = 1; if(thread处于阻塞状态) 唤醒线程thread; } } 

unpark一定会将permit置为1,如果线程阻塞,再将其唤醒。从实现可见,无论调用几次unparkpermit只能为1。

park/unpark的实验

public class test3 { 
    public static void main(String[] args) throws InterruptedException { 
    LockSupport.park(); //因为此时permit为0且中断状态为false,所以阻塞 } } 
public class test3 { 
    public static void main(String[] args) throws InterruptedException { 
    LockSupport.unpark(Thread.currentThread()); //置permit为1 LockSupport.park(); //消耗掉permit后,直接返回了 } } 
public class test3 { 
    public static void main(String[] args) throws InterruptedException { 
    LockSupport.unpark(Thread.currentThread()); LockSupport.park(); //消耗掉permit后,直接返回了 LockSupport.park(); //此时permit为0,中断状态为false,必然会阻塞 } } 
public class test3 { 
    public static void main(String[] args){ 
    Thread main = Thread.currentThread(); new Thread(new Runnable() { 
    @Override public void run() { 
    System.out.println("子线程开始睡觉"); try { 
    Thread.sleep(1000);//睡一下保证是在main线程park后,才去unpark main线程 } catch (InterruptedException e) { 
    System.out.println(Thread.currentThread().getName()+"抛出了中断异常"); } System.out.println("子线程睡醒了,开始unpark main线程"); LockSupport.unpark(main); } }).start(); LockSupport.park(); //此时permit为0,中断状态为false,必然会阻塞 //被子线程unpark后,从上一句被唤醒,继续执行。此时permit还是为0,中断状态为false。 LockSupport.park(); //此时permit为0,中断状态为false,必然会阻塞 } } 

interrupt()与park()

interrupt()实现的伪代码

interrupt(){ 
    if(中断状态 == false) { 
    中断状态 = true; } unpark(this); //注意这是Thread的成员方法,所以我们可以通过this获得Thread对象 } 

interrupt()会设置中断状态为true。注意,interrupt()还会去调用unpark的,所以也会把permit置为1的。

interrupt()实验

public class test3 { 
    public static void main(String[] args) throws InterruptedException { 
    Thread.currentThread().interrupt(); LockSupport.park(); //消耗掉permit后,直接返回了 } } 

上面程序执行后,程序运行结束。因为park执行时permit为1,直接返回了。

public class test3 { 
    public static void main(String[] args) throws InterruptedException { 
    Thread.currentThread().interrupt(); LockSupport.park(); //消耗掉permit后,直接返回了 LockSupport.park(); //因为中断状态 == true,直接返回了 LockSupport.park(); //同上 } } 

上面程序执行后,程序运行结束。马上无论怎么park都无法阻塞线程了,因为此时线程的中断状态为true,函数直接返回了。

public class test3 { 
    public static void main(String[] args) throws InterruptedException { 
    Thread main = Thread.currentThread(); new Thread(new Runnable() { 
    @Override public void run() { 
    System.out.println("马上开始睡觉"); try { 
    Thread.sleep(1000);//睡一下保证是在main线程阻塞后,才去中断main线程 } catch (InterruptedException e) { 
    e.printStackTrace(); } System.out.println("睡醒了,开始中断main线程"); main.interrupt(); } }).start(); LockSupport.park(); //此时permit为0,中断状态为false,必然会阻塞 //被子线程中断后,从上一句被唤醒,继续执行。此时permit为0,中断状态为true。 LockSupport.park(); //因为中断状态 == true,直接返回了 LockSupport.park(); //同上 } } 

sleep()与interrupt()

sleep()实现的伪代码

sleep(){ 
   //这里我忽略了参数,假设参数是大于0的即可 if(中断状态 == true) { 
    中断状态 = false; throw new InterruptedException(); } 线程开始睡觉; if(中断状态 == true) { 
    中断状态 = false; throw new InterruptedException(); } } 

sleep()会去检测中断状态,如果检测到了,那就消耗掉中断状态后,抛出中断异常。但sleep()不会去动permit

sleep()实验

public class test3 { 
    public static void main(String[] args){ 
    Thread.currentThread().interrupt(); try { 
    Thread.sleep(1000); // 消耗掉中断状态后,抛出异常 } catch (InterruptedException e) { 
    e.printStackTrace(); } } } 

上面程序执行后,抛出异常,程序运行结束。

public class test3 { 
    public static void main(String[] args){ 
    Thread.currentThread().interrupt(); try { 
    Thread.sleep(1000); // 消耗掉中断状态后,抛出异常 } catch (InterruptedException e) { 
    e.printStackTrace(); } LockSupport.park(); //消耗掉permit } } 

上面程序执行后,抛出异常,程序运行结束。

public class test3 { 
    public static void main(String[] args){ 
    Thread.currentThread().interrupt(); try { 
    Thread.sleep(1000);//消耗掉中断状态 } catch (InterruptedException e) { 
    e.printStackTrace(); } LockSupport.park(); //消耗掉permit LockSupport.park(); //因为此时permit为0且中断状态为false,所以阻塞 } } 

上面程序执行后,抛出异常,程序不会运行结束。

public class test3 { 
    public static void main(String[] args){ 
    Thread main = Thread.currentThread(); new Thread(new Runnable() { 
    @Override public void run() { 
    System.out.println("子线程开始睡觉"); try { 
    Thread.sleep(3000);//睡一下保证是在main线程sleep后,才去中断main线程 } catch (InterruptedException e) { 
    System.out.println(Thread.currentThread().getName()+"抛出了中断异常"); } System.out.println("子线程睡醒了,开始中断main线程"); main.interrupt(); } }).start(); try { 
    System.out.println("主线程开始睡觉"); Thread.sleep(5000); //main线程开始睡觉 // 当被中断唤醒后,会消耗掉中断状态。唤醒后继续执行 } catch (InterruptedException e) { 
    System.out.println(Thread.currentThread().getName()+"抛出了中断异常"); } LockSupport.park(); //消耗掉permit后,直接返回了 LockSupport.park(); //此时permit为0,中断状态为false,必然会阻塞 } } 

wait/join 效果同sleep

public class test3 { 
    public static void main(String[] args){ 
    Thread.currentThread().interrupt(); Object lock = new Object(); synchronized (lock) { 
    try { 
    lock.wait(); //消耗掉中断状态 } catch (InterruptedException e) { 
    e.printStackTrace(); } } LockSupport.park(); //消耗掉permit LockSupport.park(); //此时permit为0,中断状态为false,必然会阻塞 } } 

上面程序执行后,抛出异常,程序不会运行结束。

public class test3 { 
    public static void main(String[] args){ 
    Thread.currentThread().interrupt(); Thread thread = new Thread(()->{ 
    while (true) { 
   } }); thread.start(); try { 
    thread.join(); //消耗掉中断状态 } catch (InterruptedException e) { 
    e.printStackTrace(); } LockSupport.park(); //消耗掉permit LockSupport.park(); //此时permit为0,中断状态为false,必然会阻塞 } } 

上面程序执行后,抛出异常,程序不会运行结束。通过Dump Threads后,可以发现main处于WAITING (parking)状态,即阻塞状态。

总结

  • park调用后一定会消耗掉permit,无论unpark操作先做还是后做。
  • 如果中断状态为true,那么park无法阻塞。
  • unpark会使得permit为1,并唤醒处于阻塞的线程。
  • interrupt()会使得中断状态为true,并调用unpark
  • sleep() / wait() / join()调用后一定会消耗掉中断状态,无论interrupt()操作先做还是后做。

关于这一点,“如果中断状态为true,那么park无法阻塞”。在AQS源码里的acquireQueued里,由于acquireQueued是阻塞式的抢锁,线程可能重复着 阻塞->被唤醒 的过程,所以在这个过程中,如果遇到了中断,一定要用Thread.interrupted()中断状态消耗掉,并将这个中断状态暂时保存到一个局部变量中去。不然只要遇到中断一次后,线程在抢锁失败后却无法阻塞了。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/218375.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 上午7:24
下一篇 2026年3月18日 上午7:25


相关推荐

  • 设置webhook_Webhook模式

    设置webhook_Webhook模式Webhook 模式浏览 0 扫码分享 2019 10 2717 16 53origin last modified 2018 10 0115 59 9696 译文原文英文原文版权声明 本文为码农文档原创译文 遵循 CC4 0BY NC SA 版权协议 转载请附上原文出处链接和本声明 公告 如果您也想加入翻译队伍 或者您有相关中文文档想要贡献给大家 请联系 coderdocumen 1

    2026年3月18日
    2
  • zencart网店模板

    zencart网店模板帮朋友做了个zencart网店模板 香香礼品网:http://myliba.com/

    2022年7月27日
    8
  • 最受欢迎的 Linux 怎么是它,Ubuntu 排第六

    最受欢迎的 Linux 怎么是它,Ubuntu 排第六????作者:Linux猿????简介:CSDN博客专家????,C/C++、面试、刷题、算法尽管咨询我,关注我,有问题私聊!????关注专栏:Linux(优质好文持续更新中……)????不多废话,先来看一下排名:图1DistroWatch网站排名上面是排名前30位的最受欢迎的Linux操作系统,可以看到,比较熟悉的操作系统也名列前茅,比如:Ubuntu、Debian、Fedora、Arch、CentOS、UbuntuKylin以及deepin等。上面的排名是

    2022年6月10日
    35
  • Tomcat安装及环境配置(详细教程)

    Tomcat安装及环境配置(详细教程)Tomcat安装及环境配置(详细教程)tomcat下载及安装1、进入官网www.apache.org,找到Projects中的projectList找到tomcatDownload下边的版本,这里最新的是10.0x,但是一般不选最新版本,我选择的是8.5x,所以点击Download下边的Tomcat8这里我选择了64位的Windows版本,根据自己的需求下载。(建议:最好别放到C盘)下载之后,解压配置环境变量系统变量添加变量:变量名:CATAL..

    2022年4月30日
    68
  • UNIX命令

    UNIX命令 一、UNIX命令格式1、UNIX命令提示符   在命令行下,操作系统会显示一提示符,提示用户在此提示符后可以输入一行命令。不同的Shell有不同的缺省提示符:       BShell和KShell的缺省提示符为”$”;       CShell的缺省提示符为”%”;       但当以root用户登录时,系统提示符统一缺省为”#”。用户可以更改自己的缺省Shell和提

    2022年6月1日
    32
  • css渐变

    css渐变渐变分为两种 一 线性渐变 lineargradie 向下 向上 向左 向右 对角二 径向渐变 radialgradie 由他们的中心定义 1 线性渐变 默认是从上向下变化 name background image linear gradient toright color color 向右变化 也可以将 right 换成其他几个方向哦 或者也可以 righttop 这样就是右上啦 2 径向渐变 name background image

    2026年3月19日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号