park()

Thread t1 = new Thread(() -> {System.out.println("t1 park");LockSupport.park(); // 没有许可,线程将会被阻塞
}, "t1");t1.start();TimeUnit.MILLISECONDS.sleep(500); // 确保 t1 执行

unpark()

如果当前线程 thisThread 调用 LockSupport.park() 被阻塞,那么调用 LockSupport.unpark(thisThread) 将解除其阻塞; 如果线程没有被阻塞而调用了 LockSupport.unpark(thisThread),则保证它下一次调用 LockSupport.park() 不会阻塞,但许可只能拥有一个

Thread t1 = new Thread(() -> {System.out.println("t1 unpark");LockSupport.unpark(Thread.currentThread()); // t1先获取许可System.out.println("t1 park");LockSupport.park(); // 拥有许可,线程将不会被阻塞,然后会释放许可
}, "t1");t1.start();TimeUnit.MILLISECONDS.sleep(500);
Thread t1 = new Thread(() -> {System.out.println("t1 unpark");LockSupport.unpark(Thread.currentThread()); // t1先获取许可System.out.println("t1 park");LockSupport.park(); // 拥有许可,线程将不会被阻塞,然后会释放许可System.out.println("t1 再次 park 阻塞");LockSupport.park(Thread.currentThread()); // 线程将会被阻塞
}, "t1");t1.start();TimeUnit.MILLISECONDS.sleep(500);
Thread t1 = new Thread(() -> {System.out.println("t1 unpark1");LockSupport.unpark(Thread.currentThread()); // t1获取 1 次许可System.out.println("t1 unpark2");LockSupport.unpark(Thread.currentThread()); // t1获取 2 次许可System.out.println("t1 unpark3");LockSupport.unpark(Thread.currentThread()); // t1获取 3 次许可System.out.println("t1 park");LockSupport.park(); // 拥有许可,线程将不会被阻塞,然后会释放许可System.out.println("t1 再次 park");LockSupport.park(Thread.currentThread()); // 线程将会被阻塞,许可只能用一次
}, "t1");t1.start();TimeUnit.MILLISECONDS.sleep(500);

中断状态为 true 线程不会 park

中断状态为 true 时,不会被 park,无论调用几次

// 中断对 park 的影响Thread t1 = new Thread(() -> {System.out.println("interrupt t1");Thread.currentThread().interrupt();System.out.println(Thread.currentThread().isInterrupted()); // trueSystem.out.println("park t1");LockSupport.park(); // 中断标记为true,线程将不会被阻塞System.out.println("t1 没有被 park");System.out.println("park t1");LockSupport.park(); // 线程还是不会阻塞System.out.println("t1 还是没有被 park");}, "t1");t1.start();TimeUnit.MILLISECONDS.sleep(500); // 确保 t1 执行

中断后调用 Thread.interrupted() 清除中断状态

Thread t1 = new Thread(() -> {System.out.println("interrupt t1");Thread.currentThread().interrupt();System.out.println(Thread.currentThread().isInterrupted()); // trueSystem.out.println(Thread.interrupted()); // trueSystem.out.println(Thread.currentThread().isInterrupted()); // falseSystem.out.println("park t1");LockSupport.park(); // 线程不阻塞System.out.println("第一次 park t1 没有阻塞");System.out.println("t1 再次 park 阻塞");LockSupport.park(); // 线程阻塞System.out.println("第二次 park t1 阻塞");}, "t1");t1.start();TimeUnit.MILLISECONDS.sleep(500); // 确保 t1 执行

可以看到,清除中断状态后,第一次 park 还是不起作用的,第二次 park 线程被阻塞了

不阻塞线程直接调用 Thread.interrupted(),第一次调用 park 就会阻塞

Thread t1 = new Thread(() -> {System.out.println(Thread.interrupted()); // falseSystem.out.println(Thread.currentThread().isInterrupted()); // falseSystem.out.println("park t1");LockSupport.park(); // 线程阻塞System.out.println("第一次 park t1 阻塞");}, "t1");t1.start();TimeUnit.MILLISECONDS.sleep(500); // 确保 t1 执行

因此线程中断并清除中断状态后,第二次调用 park 会被阻塞

但是还有一种情况:先 park,然后在外部(主线程)中断,中断了 park 后,中断标记还是 true,但是此时清除中断状态后的第一次 park 就会阻塞

Thread t1 = new Thread(() -> {System.out.println(Thread.currentThread().isInterrupted()); // falseLockSupport.park();System.out.println(Thread.currentThread().isInterrupted()); // trueSystem.out.println(Thread.interrupted()); // trueSystem.out.println(Thread.currentThread().isInterrupted()); // falseSystem.out.println("park t1");LockSupport.park(); // 线程阻塞}, "t1");t1.start();TimeUnit.MILLISECONDS.sleep(2000);t1.interrupt();

出现这两种情况不同的原因:
unpark() 将 _counter 置为 1,然后判断之前 _counter 是否为 0,如果为 0 则唤醒线程


void Parker::unpark() {//定义两个变量,staus用于判断是否获取锁
int s, status ;
//获取锁
status = pthread_mutex_lock(_mutex);
//判断是否成功
assert (status == 0, "invariant") ;
//存储原先变量_counter
s = _counter;
//把_counter设为1
_counter = 1;
if (s < 1) {// thread might be parkedif (_cur_index != -1) {// thread is definitely parkedif (WorkAroundNPTLTimedWaitHang) {status = pthread_cond_signal (&_cond[_cur_index]);assert (status == 0, "invariant");status = pthread_mutex_unlock(_mutex);assert (status == 0, "invariant");} else {status = pthread_mutex_unlock(_mutex);assert (status == 0, "invariant");status = pthread_cond_signal (&_cond[_cur_index]);assert (status == 0, "invariant");}} else {//释放锁pthread_mutex_unlock(_mutex);assert (status == 0, "invariant") ;}
} else {//释放锁pthread_mutex_unlock(_mutex);assert (status == 0, "invariant") ;
}
}

park() 首先会检查 _counter是否 > 0,如果 > 0,直接返回;还会检查中断标志位是否为 true,如果为 true 也会返回

  void Parker::park(bool isAbsolute, jlong time) {// Ideally we'd do something useful while spinning, such// as calling unpackTime().// Optional fast-path check:// Return immediately if a permit is available.// We depend on Atomic::xchg() having full barrier semantics// since we are doing a lock-free update to _counter.// 检查许可即 _counter 是否 > 0,> 0 直接 return,并原子性地把 _counter 改为 0if (Atomic::xchg(0, &_counter) > 0) return;  Thread* thread = Thread::current();assert(thread->is_Java_thread(), "Must be JavaThread");JavaThread *jt = (JavaThread *)thread;// Optional optimization -- avoid state transitions if there's an interrupt pending.// Check interrupt before trying to wait// 检查中断标志 如果 true 也返回if (Thread::is_interrupted(thread, false)) {  return;}

而 interrupt() 不仅会将中断标记设为 true,还会将 _counter 置为 1,下面是 interrupt 的 c++ 源码

void os::interrupt(Thread* thread) {assert(Thread::current() == thread || Threads_lock->owned_by_self(),"possibility of dangling Thread pointer");OSThread* osthread = thread->osthread();if (!osthread->interrupted()) {osthread->set_interrupted(true);// More than one thread can get here with the same value of osthread,// resulting in multiple notifications.  We do, however, want the store// to interrupted() to be visible to other threads before we execute unpark().OrderAccess::fence();ParkEvent * const slp = thread->_SleepEvent ;if (slp != NULL) slp->unpark() ;}// For JSR166. Unpark even if interrupt status already was setif (thread->is_Java_thread())((JavaThread*)thread)->parker()->unpark(); // 调用 unpark()ParkEvent * ev = thread->_ParkEvent ;if (ev != NULL) ev->unpark() ;}

而 Thread.interrupted() 虽然会清除中断标记,但并不会将 _counter 置为 0

public static boolean interrupted() {return currentThread().isInterrupted(true);
}
bool os::is_interrupted(Thread* thread, bool clear_interrupted) {assert(Thread::current() == thread || Threads_lock->owned_by_self(),"possibility of dangling Thread pointer");OSThread* osthread = thread->osthread();bool interrupted = osthread->interrupted();if (interrupted && clear_interrupted) {osthread->set_interrupted(false); // 清除中断标记// consider thread->_SleepEvent->reset() ... optional optimization}return interrupted;
}

因此:
第一种情况:调用 interrupted 后虽然清除中断标记,但并不会将 _counter 置为 0,所以第一次调用 park 时检查 _counter 还是 1,不会阻塞

第二种情况:

  • 首先 park 阻塞,此时 _counter 是为 0(因为没调用 unpark,也没调用 interrupt呢)
  • 但是之后调用 interrupt 将中断标记置为 true,_counter 又会置为 1,然后 park 被中断,此时 _counter 肯定会变为 0 了(这时相当于调用了 unpark,所以阻塞被唤醒,但是关于 unpark 是如何解除 park 的还不太清除,我猜可能是在 park 过程中会一直检查 _counter 的值,一旦 发现是 1 就立即改为 0 并终止 park)
  • 调用 Thread.interrupted() 清除了中断状态后,_counter 为 0 ,中断标记也为 true 了,所以再调用 park 会阻塞

更新
终于在知乎一个大佬的文章里找到了唤醒后将 _counter 改为 0 的代码了,具体可以看原文(参考文献[1]),由于我也看不懂 c++ 代码,大佬说啥就是啥吧

参考文献

[1] 令人困惑的LockSupport的park()和unpark()

LockSupport 的 park 和 unpark 以及线程中断对 park 的影响相关推荐

  1. LockSupport的park和unpark

    LockSupport是JDK中比较底层的类,用来创建锁和其他同步工具类的基本线程阻塞原语. Java锁和同步器框架的核心AQS:AbstractQueuedSynchronizer,就是通过调用Lo ...

  2. Java并发学习(五)-LockSupport里面的park和unpark

    学习AQS源码时候,发现当判断队列需要入队挂起时,都是调用LockSupport里面的park和unpark方法,例如: //park并且检查是否中断 private final boolean pa ...

  3. LockSupport park和unpark

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

  4. java lock park_java并发编程-LockSupport中park与unpark基本使用与原理简单分析

    文章目录 java并发编程原理之---park与unpark 基本使用 情况一,先park再unpark,代码举例与分析 情况二,先unpark再park,代码举例与分析 特点 原理之park &am ...

  5. java帐篷_Java多线程之 Park和Unpark(十四)

    介绍 Park 和 Unpark 均是 LockSupport 类中的方法 //暂停当前线程 LockSupport.park(); //恢复某个线程 LockSupport.unpark(暂停线程对 ...

  6. java 中断线程 wait_Java 线程中断(interrupt)与阻塞 (park)的区别

    很多Java开发人员(包括我),尤其是刚进入软件行业的新手,认为Java设置线程中断就是表示线程停止了,不往前执行了, Thread.currentThread().interrupt() 其实不是这 ...

  7. 四、LockSupport与线程中断

    一.线程中断机制 1.什么是中断 1.中断可以理解为线程的一个标志位,它表示了一个运行中的线程是否被其他线程进行了中断操作.中断好比其他线程对该线程打了一个招呼,其他线程通过调用该线程的interru ...

  8. LockSupport 以及 park、unpark 方法

    一.LockSupport 是 jsr 166 中新增的 juc 工具类. LockSupport 类主要用于创建锁和其他同步类来实现线程阻塞. 这个类与他使用的每个线程进行关联, 如果可用就立即 p ...

  9. Java LockSupport以及park、unpark方法源码深度解析

    介绍了JUC中的LockSupport阻塞工具以及park.unpark方法的底层原理,从Java层面深入至JVM层面. 文章目录 1 LockSupport的概述 2 LockSupport的特征和 ...

最新文章

  1. linux 虚拟机不能启动不了系统,虚拟机更改linux初始启动5,出现无法启动现象
  2. iOS9 白名单问题 -canOpenURL: failed for URL: xx - error:This app is not allowed to query for scheme x...
  3. 2017已过半,这半年,你累吗?
  4. 数据库练习(二)三个数据库根据指定id获取name和存储数据库名称
  5. 菜鸟教程php上传图片,PHP 文件上传
  6. 【2014华为校园招聘成都上机笔试题目】
  7. 不要犹豫 了解这些即可玩转阿里云ODPS
  8. java ajaxsubmit_ajaxSubmit返回JSON格式
  9. CPL7用户指南(中文版)_第二章 CPL7 用户指南
  10. 微软的产品激活中心电话
  11. 第二届网络安全、人工智能与数字经济国际学术会议(CSAIDE 2023)
  12. MTK6577 编译报错
  13. Could not find conduit initiator for address
  14. 上古卷轴5:重制版Skyrim Together用MO无法替换游戏菜单Menu图像LOGO的问题解决
  15. 棱镜-分布式实时计算的跟踪校验系统
  16. 干货 | Trip.com APP QUIC应用和优化实践
  17. Android内嵌H5,安卓手机返回键点击无反应、苹果手机返回键正常情况解决方案
  18. oracle中的表别名怎么,Oracle 表别名
  19. 【Python爬虫】图文教学 爬取并汇总至Excel:高考志愿中的所有专业大类、具体专业的各项信息
  20. 在国内当个程序员究竟钱途如何?2011程序员薪资调查报告

热门文章

  1. 关于HTTP重定向至HTTPS
  2. 论文阅读笔记 | 三维目标检测——PointRCNN
  3. 生成微信支付二维码及微信支付
  4. WebRTC初学Demo
  5. 从计算机应用的角度进行分类,计算机考试文档
  6. cordova App脚手架
  7. 微信小程序【人脸识别功能】
  8. Rockland检测开发丨Rockland 免疫分析开发方案
  9. c语言简单的字符串处理
  10. detecting text in natural image with connectionist text proposal network