文章目录

  • 原理简单讲解
  • 调用park()与unpark()
    • park/unpark实现的伪代码
    • park/unpark的实验
  • interrupt()与park()
    • interrupt()实现的伪代码
    • interrupt()实验
  • sleep()与interrupt()
    • sleep()实现的伪代码
    • sleep()实验
    • wait/join 效果同sleep
  • 总结

原理简单讲解

首先声明,本文不会去贴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,所以阻塞}
}

上面程序执行后,程序不会运行结束,main线程阻塞。
原因是,线程默认的permit是0,中断状态为false,所以会阻塞当前线程;

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

上面程序执行后,程序运行结束。
原因是LockSupport.unpark(Thread.currentThread())执行后,会使得main线程的permit为1。而park时发现这个permit为1时,就会消耗掉这个permit,然后直接返回,所以main线程没有阻塞。

public class test3 {public static void main(String[] args) throws InterruptedException {LockSupport.unpark(Thread.currentThread());LockSupport.park();  //消耗掉permit后,直接返回了LockSupport.park();  //此时permit为0,中断状态为false,必然会阻塞}
}

上面程序执行后,程序不会运行结束,main线程阻塞。
原因是第二次park时,permit为0了,中断状态为false,所以会阻塞当前线程;

public class test3 {public static void main(String[] args){Thread main = Thread.currentThread();new Thread(new Runnable() {@Overridepublic 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,必然会阻塞}
}

上面程序执行后,程序不会运行结束,main线程阻塞。
这个程序同上,只是之前的版本都是先unpark,再park。现在保证是,main线程先park后,再去unpark main线程。

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() {@Overridepublic 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();  //同上}
}

上面程序执行后,程序运行结束。
这个程序同上,只是之前的版本都是先中断,再park。现在保证是,main线程先阻塞后,再去中断main线程。

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();  //消耗掉permitLockSupport.park();  //因为此时permit为0且中断状态为false,所以阻塞}
}

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

public class test3 {public static void main(String[] args){Thread main = Thread.currentThread();new Thread(new Runnable() {@Overridepublic 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,必然会阻塞}
}

上面程序执行后,抛出异常,程序不会运行结束。
这个程序同上,只是之前的版本都是先中断,再sleep。现在保证是,main线程先sleep后,再去中断main线程。

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();  //消耗掉permitLockSupport.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();  //消耗掉permitLockSupport.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()中断状态消耗掉,并将这个中断状态暂时保存到一个局部变量中去。不然只要遇到中断一次后,线程在抢锁失败后却无法阻塞了。

interrupt()中断对LockSupport.park()的影响相关推荐

  1. LockSupport 的 park 和 unpark 以及线程中断对 park 的影响

    park() Thread t1 = new Thread(() -> {System.out.println("t1 park");LockSupport.park(); ...

  2. LockSupport park和unpark

    前言 在上一篇文章线程池返回值Future中,源码分析线程池结果获取阻塞的原因. LockSupport.unpark(t); LockSupport.parkNanos(this, nanos);或 ...

  3. Java中的线程休眠大法系列(三)LockSupport.park()

    文章目录 前言 一.看看JDK的代码注释 二.案例 1. 查看线程状态 2.线程被打断,不抛异常 2.是否会释放锁 3.传递自定义数据 总结 前言 Java的线程休眠我们从Thread.sleep到O ...

  4. Thread.sleep() / Object.wait() / Condition.await() / LockSupport.park() / LockSupport.unpark() 区别

    转自:https://www.cnblogs.com/tong-yuan/p/11768904.html Thread.sleep()和Object.wait()的区别 首先,我们先来看看Thread ...

  5. thread.sleep会释放锁吗_面试 LockSupport.park()会释放锁资源吗?

    (手机横屏看源码更方便) 引子 大家知道,我最近在招人,今天遇到个同学,他的源码看过一些,然后我就开始了AQS连环问. 我:说说AQS的大致流程? 他:AQS包含一个状态变量,一个同步队列--bala ...

  6. 让面试官心服口服:Thread.sleep、synchronized、LockSupport.park的线程阻塞有何区别?

    前言 在日常编码的过程中,我们经常会使用Thread.sleep.LockSupport.park()主动阻塞线程,或者使用synchronized和Object.wait来阻塞线程保证并发安全.此时 ...

  7. Android Thread interrupt 中断JAVA线程(转)

    转载自:http://hi.baidu.com/%E3%C6%CE%C4%B7%E5/blog/item/d8959f1b6716c8168618bfbb.html 假如我们有一个任务如下,交给一个J ...

  8. 用interrupt()中断Java线程

    http://hapinwater.iteye.com/blog/310558 最近在学习Java线程相关的东西,和大家分享一下,有错误之处欢迎大家指正. 假如我们有一个任务如下,交给一个Java线程 ...

  9. 小米集团王嵋因错误表达致歉并请辞;亚马逊云服务出现中断,许多网站受到影响;deepin 深度系统更新发布|极客头条...

    整理 | 郑丽媛 头图 | CSDN 下载自东方 IC 快来收听极客头条音频版吧,智能播报由出门问问「魔音工坊」提供技术支持. 「极客头条」-- 技术人员的新闻圈! CSDN 的读者朋友们早上好哇,「 ...

最新文章

  1. SharePoint 2007 SDK 有了1.1版本
  2. application.xml定时
  3. Java中获取近七天的日期(包含今天)
  4. C#和Java的代码转换工具(开源)CSharpJavaMerger Framework
  5. Java学习 第四章 java面向对象(二)
  6. jQuery 筛选
  7. python——作用域 == is
  8. ipad怎么连接电脑_苹果连接电脑没反应怎么办
  9. Linux常用基本命令:三剑客命令之-awk内置函数用法
  10. C#一个完整的电子邮件操作类
  11. 实力封装:Unity打包AssetBundle(三)
  12. 蓝蓝设计 使用全屏照片的网页设计欣赏
  13. 基于机器学习的笑脸检测
  14. RNNoise超详细解析
  15. 50 Android hacks(hack 1)
  16. CookieSession
  17. 阿里架构师:双十一「大促」,多亏了 Node.js
  18. android rom签名 作用,Ubuntu下折腾Android笔记(一)——ROM 签名 | 翅膀~
  19. 爱盈利app推广专家相关介绍
  20. Functionlan通过星际文件系统免费使用云应用程序

热门文章

  1. Python代码实现图像增强(线性变换、对数变换、幂律变换、分段线性变换、灰度级分层、直方图均衡化、平滑滤波器、锐化滤波器)
  2. python 列表操作模块_Python:使用模块laspy的列表理解问题
  3. WPF教程(六)二进制资源
  4. MySQL将字段数据自增自减
  5. idea出现clear read-only status问题解决方案
  6. laravel下视图间共享数据
  7. Linux——磁盘分区与挂载
  8. Android studio 百度地图开发(5)查询周边服务(加油站)
  9. 微信小程序加油站小程序带后台
  10. Cobalt Strike:解密 DNS 流量——第 5 部分