ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Unsafe类park和unpark方法源码深入分析(mutex+cond)-转载

2022-08-03 14:03:04  阅读:221  来源: 互联网

标签:Unsafe park 博客 源码 cond pthread mutex wait


转载:https://blog.csdn.net/saintyyu/article/details/107426428

说明:本篇博客整理自文末的多篇参考博客(每篇博客各有侧重)。本文结合源码对Unsafe的park和unpark方法进行了完整全面的梳理,并对部分参考博客中存在的错误描述进行说明。

LockSupport类的park/unpark方法可以更简单灵活地实现synchronized关键字 + Object类的wait/nofity方法所达到的让线程按照指定顺序执行的效果(详见参考博客1),而LockSupport底层就是通过调用Unsafe类的park和unpark方法来实现的。

由参考博客2可知(建议先读完参考博客2),每个Thread都包含一个Parker成员变量,Unsafe的park/unpark方法最终就是调用Parker类的park/unpark方法。在Parker的park方法中,会调用pthread_mutex_trylock方法,该方法实际上是pthread_mutex_lock方法的非阻塞版本。也就是说,不同于pthread_mutex_lock方法在获取不到互斥锁时会阻塞住,调用pthread_mutex_trylock方法不会阻塞当前线程,而是立即返回一个值来描述互斥锁是否获取成功(参考博客3中说LockSupport和synchronized一样,都是通过调用pthread_mutex_lock方法来阻塞当前线程,这个说法是不对的,实际上synchronized确实是通过调用pthread_mutex_lock方法来阻塞当前线程,而LockSupport是通过调用atomic_load_acquire方法阻塞等待唤醒信号,后面会详细介绍)。

既然LockSupport没有调用pthread_mutex_lock方法,那么LockSupport的park方法到底是阻塞在什么方法上呢?由参考博客2可知,答案是pthread_cond_wait方法。遗憾的是参考博客2并未给出pthread_cond_wait方法的具体实现。这里先给出一张park和unpark底层的实现时序图:

 

由图可知,pthread_cond_wait方法会先操作条件变量,然后释放锁,接着阻塞当前线程,等待condition的唤醒信号。这里之所以要释放锁,是为了让当前的阻塞线程和唤醒线程互斥地访问并操作条件变量(该图中调用pthread_cond_signal的线程在调用该方法之前会先修改条件变量,图中未画出),否则就可能会出现唤醒消息丢失(详见参考博客6)。

当唤醒线程修改了条件变量、执行完pthread_cond_signal方法,并释放锁之后,当前被阻塞的线程从阻塞状态恢复到执行状态,此时会重新竞争互斥锁,竞争到互斥锁之后会再次修改条件变量(修改_counter等变量,就是为了标记锁的占用情况,详见参考博客2中的源码注释)。

为了进一步弄清楚pthread_cond_wait方法的是如何阻塞的,我阅读了pthread_cond_wait的源码(参考博客5),核心流程梳理如下(因为__pthread_cond_wait调用了__pthread_cond_wait_common,所以重点看后者,以下只截取核心思路):

//1、条件变量入等待队列
uint64_t wseq = __condvar_fetch_add_wseq_acquire (cond, 2);
//2、释放互斥锁
err = __pthread_mutex_unlock_usercnt (mutex, 0);
//3、阻塞并等待唤醒信号(实际可能得到关闭信号)
unsigned int signals = atomic_load_acquire (cond->__data.__g_signals + g);

//4、先循环等待maxspin次,获取signal
unsigned int spin = maxspin;
while (signals == 0 && spin > 0)
{
/* Check that we are not spinning on a group that's already
closed. */
if (seq < (__condvar_load_g1_start_relaxed (cond) >> 1))
goto done;
/* TODO Back off. */
/* Reload signals. See above for MO. */
signals = atomic_load_acquire (cond->__data.__g_signals + g);
spin--;
}
//5、阻塞获取互斥锁
err = futex_wait_cancelable (
cond->__data.__g_signals + g, 0, private);
//6、获取互斥锁后执行条件变量修改操作
......
由此可见,上面的核心源码和上面的示意图是相匹配的。

最后,详细阅读参考博客6中的源码,结合参考博客7可知,阻塞机制底层是Linux内核基于等待队列wait_queue和等待事件wait_event来实现的。

参考博客:

1、自己动手写把”锁”---LockSupport深入浅出 - 清泉^_^ - 博客园 自己动手写把锁--LockSupport深入浅出

2、jdk1.8 Unsafe类 park和unpark方法解析_lcjmsr的博客-CSDN博客_unsafe的park jdk1.8 Unsafe类 park和unpark方法解析

3、Synchronized 和 Lock 锁在JVM中的实现原理以及代码解析 - 云+社区 - 腾讯云 Synchronized 和 Lock 锁在JVM中的实现原理以及代码解

4、pthread条件变量condition(配合mutex锁使用),经典,有图_OpenWrt_新浪博客 pthread条件变量condition配合mutex锁使用

5、pthread_cond_wait.c source code [glibc/nptl/pthread_cond_wait.c] - Woboq Code Browser glibc/nptl/pthread_cond_wait.c源码

6、pthread_cond_wait 为什么需要传递 mutex 参数? - 知乎 pthread_cond_wait 为什么需要传递 mutex 参数

7、linux中的阻塞机制及等待队列 - touchcode - 博客园 Linux中阻塞队列及等待机制

8、pthread_mutex_lock源码分析 - wa小怪兽 - 博客园 pthread_mutex_lock源码分析

9、操作系统中多线程的个人理解:mutex和condition - 知乎 操作系统中多线程的个人理解:mutex和condition

10、https://www.dazhuanlan.com/2020/01/02/5e0d9b848a3b7/  hotspot Thread JavaThread OSThread

11、Thread.interrupt()源码跟踪 - 月下小魔王 - 博客园 Thread.interrrupt()源码跟踪

12、Hotspot Parker和ParkEvent 源码解析_孙大圣666的博客-CSDN博客 Hotspot Parker和ParkEvent 源码解析

13、Hotspot Thread本地方法实现 源码解析_孙大圣666的博客-CSDN博客  Hotspot Thread本地方法实现 源码解析

14、JVM锁简介:偏向锁、轻量级锁和重量级锁 - twoheads - 博客园  jvm简介:偏向锁、轻量级锁和重量级锁

15、死磕Synchronized底层实现--重量级锁 - 简书 死磕Synchronized底层实现 重量级锁

16、Linux 互斥锁、原子操作实现原理_kmcfly的博客-CSDN博客_互斥锁的实现原理

标签:Unsafe,park,博客,源码,cond,pthread,mutex,wait
来源: https://www.cnblogs.com/wangbin2188/p/16546788.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有