LockSupport(park/unpark)源码分析
来源:互联网 发布:js string转int 编辑:程序博客网 时间:2024/05/20 01:36
转载:http://www.cnblogs.com/zhizhizhiyuan/p/4966827.html
concurrent包是基于AQS (AbstractQueuedSynchronizer)框架的,AQS框架借助于两个类:
- Unsafe(提供CAS操作)
- LockSupport(提供park/unpark操作)
因此,LockSupport非常重要。
两个重点
(1)操作对象
归根结底,LockSupport.park()和LockSupport.unpark(Thread thread)调用的是Unsafe中的native代码:
//LockSupport中public static void park() { UNSAFE.park(false, 0L); }
//LockSupport中public static void unpark(Thread thread) { if (thread != null) UNSAFE.unpark(thread); }
Unsafe类中的对应方法:
//park public native void park(boolean isAbsolute, long time); //unpack public native void unpark(Object var1);
park函数是将当前调用Thread阻塞,而unpark函数则是将指定线程Thread唤醒。
与Object类的wait/notify机制相比,park/unpark有两个优点:
- 以thread为操作对象更符合阻塞线程的直观定义
- 操作更精准,可以准确地唤醒某一个线程(notify随机唤醒一个线程,notifyAll唤醒所有等待的线程),增加了灵活性。
(2)关于“许可”
在上面的文字中,我使用了阻塞和唤醒,是为了和wait/notify做对比。
其实park/unpark的设计原理核心是“许可”:park是等待一个许可,unpark是为某线程提供一个许可。
如果某线程A调用park,那么除非另外一个线程调用unpark(A)给A一个许可,否则线程A将阻塞在park操作上。有一点比较难理解的,是unpark操作可以再park操作之前。
也就是说,先提供许可。当某线程调用park时,已经有许可了,它就消费这个许可,然后可以继续运行。这其实是必须的。考虑最简单的生产者(Producer)消费者(Consumer)模型:Consumer需要消费一个资源,于是调用park操作等待;Producer则生产资源,然后调用unpark给予Consumer使用的许可。非常有可能的一种情况是,Producer先生产,这时候Consumer可能还没有构造好(比如线程还没启动,或者还没切换到该线程)。那么等Consumer准备好要消费时,显然这时候资源已经生产好了,可以直接用,那么park操作当然可以直接运行下去。如果没有这个语义,那将非常难以操作。但是这个“许可”是不能叠加的,“许可”是一次性的。
比如线程B连续调用了三次unpark函数,当线程A调用park函数就使用掉这个“许可”,如果线程A再次调用park,则进入等待状态。
Unsafe.park和Unsafe.unpark的底层实现原理
在Linux系统下,是用的Posix线程库pthread中的mutex(互斥量),condition(条件变量)来实现的。
mutex和condition保护了一个_counter的变量,当park时,这个变量被设置为0,当unpark时,这个变量被设置为1。
源码:
每个Java线程都有一个Parker实例,Parker类是这样定义的:
class Parker : public os::PlatformParker { private: volatile int _counter ; ... public: void park(bool isAbsolute, jlong time); void unpark(); ... } class PlatformParker : public CHeapObj<mtInternal> { protected: pthread_mutex_t _mutex [1] ; pthread_cond_t _cond [1] ; ... }
可以看到Parker类实际上用Posix的mutex,condition来实现的。
在Parker类里的_counter字段,就是用来记录“许可”的。
- park 过程
当调用park时,先尝试能否直接拿到“许可”,即_counter>0时,如果成功,则把_counter设置为0,并返回:
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. if (Atomic::xchg(0, &_counter) > 0) return;
如果不成功,则构造一个ThreadBlockInVM,然后检查_counter是不是>0,如果是,则把_counter设置为0,unlock mutex并返回:
ThreadBlockInVM tbivm(jt); if (_counter > 0) { // no wait needed _counter = 0; status = pthread_mutex_unlock(_mutex);
否则,再判断等待的时间,然后再调用pthread_cond_wait函数等待,如果等待返回,则把_counter设置为0,unlock mutex并返回:
if (time == 0) { status = pthread_cond_wait (_cond, _mutex) ; } _counter = 0 ; status = pthread_mutex_unlock(_mutex) ; assert_status(status == 0, status, "invariant") ; OrderAccess::fence();
- unpark 过程
当unpark时,则简单多了,直接设置_counter为1,再unlock mutex返回。如果_counter之前的值是0,则还要调用pthread_cond_signal唤醒在park中等待的线程:
void Parker::unpark() { int s, status ; status = pthread_mutex_lock(_mutex); assert (status == 0, "invariant") ; s = _counter; _counter = 1; if (s < 1) { if (WorkAroundNPTLTimedWaitHang) { status = pthread_cond_signal (_cond) ; 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) ; assert (status == 0, "invariant") ; } } else { pthread_mutex_unlock(_mutex); assert (status == 0, "invariant") ; } }
- LockSupport(park/unpark)源码分析
- Java并发学习(五)-LockSupport里面的park和unpark
- LockSupport的park和unpark的基本使用,以及对线程中断的响应性
- LockSupport的park和unpark的基本使用,以及对线程中断的响应性
- Java的LockSupport.park()实现分析
- Java的LockSupport.park()实现分析
- Java的LockSupport.park()实现分析
- Java的LockSupport.park()实现分析
- Java的LockSupport.park()实现分析
- Java的LockSupport.park()实现分析
- Java的LockSupport.park()实现分析
- LockSupport源码分析
- 4.锁--Java的LockSupport.park()实现分析
- LockSupport源码分析(JDK 1.7)
- LockSupport的源码分析(上)
- LockSupport的源码分析(中)
- Java多线程系列(五)—LockSupport源码分析
- JUC源码分析5-locks-LockSupport
- 盘点2017年热度很高的编程语言
- ObjectInputStream与ObjectOutputStream的使用问题-01
- gdb使用
- Springboot整合Redis
- 如何破解「千人千面」,深度解读用户画像
- LockSupport(park/unpark)源码分析
- 程序读取excel
- 手把手搭建最简单的SSM框架Maven,idea(spring+springmvc+mybatis)附源代码
- 通过身份证号获取年龄
- Python中字符编码与转码
- 实现盒子高度撑开,而且以最高的为高
- Android照片墙应用实现,再多的图片也不怕崩溃
- 数据规整化:清理、转换、合并、重塑
- Android入门攻略(另附一个简单的带socket的模仿QQ实例)