Unsafe和LockSupport

Java并发包下的类基本都是基于AQS (AbstractQueuedSynchronizer)框架实现的,关于AQS我在前面讲解ReentrantLock源码的文章中就已经有涉及了。

-->> 面试难点:深度解析ReentrantLock的实现原理

而AQS线程安全的实现,又是基于两个很关键的类UnsafeLockSupport,其中Unsafe主要直接提供CAS操作(关于cas,在文章 读懂AtomicInteger源码(多线程专题 )中讲解过 ),LockSupport主要提供park/unpark操作,而park/unpark最终调用还是unsafe类,所以unsafe类才是关键。

(如果不会下载JVM源码可以后台回复 “ jdk ”,获得下载压缩包)

public static void park() {UNSAFE.park(false, 0L);
}
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);

由代码可见,Unsafe类的park/unpark是native级别的实现。使用native关键字说明这个方法是原生函数,也就是这个方法是用C/C++语言实现的,并且被编译成了DLL,由java去调用。

park函数是将当前调用线程阻塞,unpark函数则是将指定线程线程唤醒。

park和unpark作用:

park是等待一个许可,unpark是为某线程提供一个许可。如果某线程A调用park,那么除非另外一个线程调用unpark(A)给A一个许可,否则线程A将阻塞在park操作上。每次调用一次park,需要有一个unpark来解锁。

并且unpark可以先于park调用,但是不管unpark先调用几次,都只提供一个许可,不可叠加,只需要一次park来消费掉unpark带来的许可,再次调用会阻塞。

Unsafe.park源码

在Linux系统下,park和unpark是用的Posix线程库pthread中的mutex(互斥量),condition(条件变量)来实现的。

简单来说,mutex和condition保护了一个叫_counter的信号量。当park时,这个变量被设置为0,当unpark时,这个变量被设置为1。当_counter=0 时线程阻塞,当_counter>0直接设为0并返回。

每个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类继承于PlatformParker,实际上时用Posix的mutex,condition来实现的。Parker类里的_counter字段,就是用来记录park和unpark是否需要阻塞的标识。

执行过程

具体的执行逻辑已经用注释标记在代码中,简要来说,就是检查_counter是不是大于0,如果是,则把_counter设置为0,返回。如果等于零,继续执行,阻塞等待。

void Parker::park(bool isAbsolute, jlong time) {//判断信号量counter是否大于0,如果大于设为0返回if (Atomic::xchg(0, &_counter) > 0) return;//获取当前线程Thread* thread = Thread::current();assert(thread->is_Java_thread(), "Must be JavaThread");JavaThread *jt = (JavaThread *)thread;//如果中途已经是interrupt了,那么立刻返回,不阻塞// Check interrupt before trying to waitif (Thread::is_interrupted(thread, false)) {return;}//记录当前绝对时间戳// Next, demultiplex/decode time argumentstimespec absTime;//如果park的超时时间已到,则返回if (time < 0 || (isAbsolute && time == 0) ) { // don't wait at allreturn;}//更换时间戳if (time > 0) {unpackTime(&absTime, isAbsolute, time);}// Enter safepoint region// Beware of deadlocks such as 6317397.// The per-thread Parker:: mutex is a classic leaf-lock.// In particular a thread must never block on the Threads_lock while// holding the Parker:: mutex.  If safepoints are pending both the// the ThreadBlockInVM() CTOR and DTOR may grab Threads_lock.//进入安全点,利用该thread构造一个ThreadBlockInVMThreadBlockInVM tbivm(jt);// Don't wait if cannot get lock since interference arises from// unblocking.  Also. check interrupt before trying waitif (Thread::is_interrupted(thread, false) || pthread_mutex_trylock(_mutex) != 0) {return;}//记录等待状态int status ;//中途再次检查许可,有则直接返回不等带。if (_counter > 0)  { // no wait needed_counter = 0;status = pthread_mutex_unlock(_mutex);assert (status == 0, "invariant") ;// Paranoia to ensure our locked and lock-free paths interact// correctly with each other and Java-level accesses.OrderAccess::fence();return;}OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);jt->set_suspend_equivalent();// cleared by handle_special_suspend_equivalent_condition() or java_suspend_self()assert(_cur_index == -1, "invariant");if (time == 0) {_cur_index = REL_INDEX; // arbitrary choice when not timed//线程条件等待 线程等待信号触发,如果没有信号触发,无限期等待下去。status = pthread_cond_wait (&_cond[_cur_index], _mutex) ;} else {_cur_index = isAbsolute ? ABS_INDEX : REL_INDEX;//线程等待一定的时间,如果超时或有信号触发,线程唤醒status = os::Linux::safe_cond_timedwait (&_cond[_cur_index], _mutex, &absTime) ;if (status != 0 && WorkAroundNPTLTimedWaitHang) {pthread_cond_destroy (&_cond[_cur_index]) ;pthread_cond_init    (&_cond[_cur_index], isAbsolute ? NULL : os::Linux::condAttr());}}_cur_index = -1;assert_status(status == 0 || status == EINTR ||status == ETIME || status == ETIMEDOUT,status, "cond_timedwait");_counter = 0 ;status = pthread_mutex_unlock(_mutex) ;assert_status(status == 0, status, "invariant") ;// Paranoia to ensure our locked and lock-free paths interact// correctly with each other and Java-level accesses.OrderAccess::fence();// If externally suspended while waiting, re-suspendif (jt->handle_special_suspend_equivalent_condition()) {jt->java_suspend_self();}
}

Unsafe.unpark源码

unpark直接设置_counter为1,再unlock mutex返回。如果_counter之前的值是0,则还要调用pthread_cond_signal唤醒在park中等待的线程。

源码如下:

void Parker::unpark() {//定义两个变量,staus用于判断是否获取锁int s, status ;//获取锁status = pthread_mutex_lock(_mutex);//判断是否成功assert (status == 0, "invariant") ;//存储原先变量_counters = _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") ;}
}

(如果不会下载JVM源码可以后台回复 “ jdk ”,获得下载压缩包)

◆ ◆ ◆  ◆ ◆

关注并后台回复 “面试” 或者  “视频”,

即可免费获取最新2019BAT

大厂面试题和大数据微服务视频

您的分享和支持是我更新的动力

·END·

后端开发技术

追求技术的深度

微信号:后端开发技术

觉得不错“在看”支持一下~

↓↓↓

park和unpark底层源码解读相关推荐

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

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

  2. hystrix 源码 线程池隔离_“池”的思想:从java线程池到数据库连接池的源码解读(1)...

    一. java线程池 带着问题: 线程是什么时候被创建的? 线程会一直循环取任务任务吗?怎么做的? 线程取不到任务会怎么样? 线程会被Runnable和Callable的异常干掉吗? 线程怎么干掉自己 ...

  3. AQS同步器源码解读有感

    1.前言 AQS(AbstractQueuedSynchronizer)是java.util.concurrent的基础.也是Doug Lea大神为广大java开发作出的卓越贡献.J.U.C中的工具类 ...

  4. Condition 源码解读

    一.Condition 在并发情况下进行线程间的协调,如果是使用的 synchronized 锁,我们可以使用 wait/notify 进行唤醒,如果是使用的 Lock 锁的方式,则可以使用 Cond ...

  5. Feflow 源码解读

    Feflow 源码解读 Feflow(Front-end flow)是腾讯IVWEB团队的前端工程化解决方案,致力于改善多类型项目的开发流程中的规范和非业务相关的问题,可以让开发者将绝大部分精力集中在 ...

  6. jdk1.8.0_45源码解读——Map接口和AbstractMap抽象类的实现

    jdk1.8.0_45源码解读--Map接口和AbstractMap抽象类的实现 一. Map架构 如上图: (01) Map 是映射接口,Map中存储的内容是键值对(key-value). (02) ...

  7. Java Review - PriorityQueue源码解读

    文章目录 Pre PriorityQueue 概述 PriorityQueue 继承关系 PriorityQueue通过用数组表示的小顶堆实现 时间复杂度 构造函数 方法 add()和offer() ...

  8. Java Review - Queue和Stack 源码解读

    文章目录 Pre 概述 Queue Deque ArrayDeque 一览 构造函数 属性 方法 addFirst() addLast() pollFirst() pollLast() peekFir ...

  9. Java Review - LinkedList源码解读

    文章目录 Pre 概述 底层数据结构-双向链表 源码解析 构造函数 方法源码分析 getFirst() getLast() remove相关方法 remove(e) remove(index) rem ...

最新文章

  1. HDU - 2586 How far away LCA+tanjar离线算法
  2. 被围绕的区域(dfs)
  3. JEPaas代码_((列表)输入字段值而改变值
  4. stream获取filter
  5. maven 解决冲突
  6. 最近完成的一个可伸缩性的WEB开发框架
  7. python使用os.system()方法进行多模块安装
  8. 如何禁止开机时进入安全模式
  9. lingo入门教程之一 --- 初识lingo
  10. 用Wineskin在Mac上运行exe文件
  11. 行列视(RCV)生产数据应用系统产品简介
  12. 计算机调剂到mba,这8种考生不能调剂!MBA/MPAcc等考研生注意
  13. 中国Linux公社校园联络员第四次全体大会会议记录
  14. 浅谈To B与To C的区别
  15. 仿造网易云音乐轮播图
  16. 如果长颈鹿哭了,它会不会要哽咽好久
  17. html5 3d自动,html5 3D微信头像自动抽奖代码
  18. AutoSAR系列讲解(入门篇)6.1-Vector的工具链简介
  19. 三次B样条插值和误差分析
  20. [软件渲染器入门]五-平面着色和高氏着色

热门文章

  1. 【100%通过率】华为OD机试真题 JS 实现【硬件产品销售方案】【2023 Q1 | 100分】
  2. C语言-谁先倒-PTA题目
  3. wordnet python处理中文与英文
  4. Unity Inverse Kinematics(IK)的使用
  5. 【UXPA大赛企业专访】Mockplus:“设计替代开发”将成为现实
  6. 产品经理 三节课P1学习笔记
  7. PCB设计标准工艺要求
  8. pango出现The encodeing of this file is not supported ,file editor try to open it with GB18030
  9. 现在完成时 - 例句分析
  10. 数学复习[高等数学,微积分,线性代数]