学习AQS源码时候,发现当判断队列需要入队挂起时,都是调用LockSupport里面的park和unpark方法,例如:

//park并且检查是否中断
private final boolean parkAndCheckInterrupt() {LockSupport.park(this);return Thread.interrupted();}...
//而需要唤醒线程进行竞争的时候,则为:
LockSupport.unpark(node.thread);...

LockSupport

LockSupport其实是一个简单的代理类,它里面的代码都是使用Unsafe类里面的native方法,这里可以简单看看sun.misc.Unsafe 本文主要学习里面的park和unpark方法。

在Unsafe里面,park和unpark分别如下定义:

    public native void park(boolean isAbsolute, long time);public native void unpark(Object thread);

其中park中变量isAbsolute代表传入的time是绝对时间还是相对时间。

unpark函数为线程提供“许可(permit)”,线程调用park函数则等待“许可”。这个有点像信号量,但是这个“许可”是不能叠加的,“许可”是一次性的。可以理解为设置一个变量0,1之间的切换。

如果线程B连续调用了多次次unpark函数,当线程A调用park函数就使用掉这个“许可”,如果线程A第二次调用park,则进入等待状态。

注意,unpark函数可以先于park调用。比如线程B调用unpark函数,给线程A发了一个“许可”,那么当线程A调用park时,它发现已经有“许可”了,那么它会马上再继续运行,也就是不会阻塞。

而如果线程A处于等待许可状态,再次调用park,则会永远等待下去,调用unpark也无法唤醒。

下面先看LockSupport里面的park和unpark定义:

    /** @param blocker the synchronization object responsible for this thread parking*/public static void park(Object blocker) {//获取当前运行线程Thread t = Thread.currentThread();//设置t中的parkBlockerOffset的值为blocker,即设置锁遍历setBlocker(t, blocker);//阻塞线程UNSAFE.park(false, 0L);//线程被释放,则将parkBlockerOffset设为nullsetBlocker(t, null);}public static void unpark(Thread thread) {if (thread != null)UNSAFE.unpark(thread);}

其中在park中,jdk文档注释为以下几种情况时,线程会被唤醒unpark:

  • Some other thread invokes {@link #unpark unpark} with the current thread as the target (调用unpark方法)
  • Some other thread {@linkplain Thread#interrupt interrupts} the current thread (被中断interrupts)
  • The call spuriously (that is, for no reason) returns.(posix condition里的”Spurious wakeup”)

park和unpark的c++实现

首先,包含park和unpark的头函数在:http://hg.openjdk.java.net/jdk8u/jdk8u40/hotspot/file/68577993c7db/src/share/vm/runtime/park.hpp

而其具体实现函数则在:http://hg.openjdk.java.net/jdk8u/jdk8u40/hotspot/file/95e9083cf4a7/src/os/solaris/vm/os_solaris.cpp

park的c++代码:

void Parker::park(bool isAbsolute, jlong time) {
//判断是否有信号量_counter是否大于0if (_counter > 0) {_counter = 0 ;OrderAccess::fence();//直接返回return ;}//获取当前线程Thread* thread = Thread::current();assert(thread->is_Java_thread(), "Must be JavaThread");JavaThread *jt = (JavaThread *)thread;//如果中途已经是interrupt了,那么立刻返回,不足色if (Thread::is_interrupted(thread, false)) {return;}//记录当前绝对时间戳timespec absTime;//如果park的超时时间已到,则返回if (time < 0) { // don't wait at allreturn;}if (time > 0) {//更换时间戳unpackTime(&absTime, isAbsolute, time);}//进入安全点,利用该thread构造一个ThreadBlockInVMThreadBlockInVM tbivm(jt);if (Thread::is_interrupted(thread, false) ||os::Solaris::mutex_trylock(_mutex) != 0) {return;}//记录等待状态int status ;//中途再次给予了许可,则直接返回不等带。if (_counter > 0)  { // no wait needed_counter = 0;status = os::Solaris::mutex_unlock(_mutex);assert (status == 0, "invariant") ;OrderAccess::fence();return;}
#ifdef ASSERTsigset_t oldsigs;sigset_t* allowdebug_blocked = os::Solaris::allowdebug_blocked_signals();thr_sigsetmask(SIG_BLOCK, allowdebug_blocked, &oldsigs);
#endifOSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);jt->set_suspend_equivalent();
#if defined(__sparc) && defined(COMPILER2)if (ClearFPUAtPark) { _mark_fpu_nosave() ; }
#endifif (time == 0) {status = os::Solaris::cond_wait (_cond, _mutex) ;} else {//time不为0,则继续等待。status = os::Solaris::cond_timedwait (_cond, _mutex, &absTime);}assert_status(status == 0 || status == EINTR ||status == ETIME || status == ETIMEDOUT,status, "cond_timedwait");
#ifdef ASSERTthr_sigsetmask(SIG_SETMASK, &oldsigs, NULL);
#endif_counter = 0 ;status = os::Solaris::mutex_unlock(_mutex);assert_status(status == 0, status, "mutex_unlock") ;if (jt->handle_special_suspend_equivalent_condition()) {jt->java_suspend_self();}OrderAccess::fence();
}

上面代码只列出了单独的park方法,上述代码中,主要通过一个_counter作为判断标志,当_counter大于0时候,就说明了拥有了“许可”,由此可以大概推断出,unpark方法则是在_counter变量上做文章。
并且,当出现了中断时,一方面,线程会被unpark,但是并不会抛出任何异常!
注:_counter是Parker里面的一个私有全局变量。

unpark的c++代码:

void Parker::unpark() {
//定义两个变量,staus用于判断是否获取锁int s, status ;//获取锁status = os::Solaris::mutex_lock (_mutex) ;//判断是否成功assert (status == 0, "invariant") ;//存储原先变量_counters = _counter;//把_counter设为1_counter = 1;//释放锁status = os::Solaris::mutex_unlock (_mutex) ;assert (status == 0, "invariant") ;if (s < 1) {//如果原先_counter信号量小于1,即为0,则进行signal操作,唤醒操作status = os::Solaris::cond_signal (_cond) ;assert (status == 0, "invariant") ;}
}

简而言之,是用mutex和condition保护了一个_counter的变量,当park时,这个变量置为了0,当unpark时,这个变量置为1。

当然在阅读park的c++函数时,还有多处地方没有结合其他类来理解,如果哪里有问题,欢迎提出来。

参考文章:
http://blog.csdn.net/hengyunabc/article/details/28126139
http://blog.csdn.net/wilsonpeng3/article/details/46387835
http://hg.openjdk.java.net/jdk8u/jdk8u40/hotspot/file/68577993c7db/src/share/vm/runtime/park.hpp
http://hg.openjdk.java.net/jdk8u/jdk8u40/hotspot/file/68577993c7db/src/share/vm/runtime/park.cpp
http://hg.openjdk.java.net/jdk8u/jdk8u40/hotspot/file/95e9083cf4a7/src/os/solaris/vm/os_solaris.cpp#l5801

Java并发学习(五)-LockSupport里面的park和unpark相关推荐

  1. 和朱晔一起复习Java并发(五):并发容器和同步器

    和朱晔一起复习Java并发(五):并发容器和同步器 本节我们先会来复习一下java.util.concurrent下面的一些并发容器,然后再会来简单看一下各种同步器. ConcurrentHashMa ...

  2. Java并发学习之玩转线程池

    2019独角兽企业重金招聘Python工程师标准>>> 线程池的使用姿势 基本上实际的项目不可能离开线程池,只是看你有没有注意到罢了 作为以业务需求为驱动,最顺溜的是写if-else ...

  3. Java并发学习二:编译优化带来的有序性问题导致的并发Bug

    Java并发学习系列文章:Java并发学习-博客专栏 今天在学习极客时间专栏:<Java并发编程实战> 第一讲01 | 可见性.原子性和有序性问题:并发编程Bug的源头中提到: 编译器及解 ...

  4. Java并发学习三:银行转账的死锁问题解决及示例

    Java并发学习系列文章:Java并发学习-博客专栏 今天在学习极客时间专栏:<Java并发编程实战> 从03 | 互斥锁(上):解决原子性问题到06 | 用"等待-通知&quo ...

  5. Java并发学习一:CPU缓存导致的可见性问题带来的并发Bug

    Java并发学习系列文章:Java并发学习-博客专栏 今天在学习极客时间专栏:<Java并发编程实战> 第一讲01 | 可见性.原子性和有序性问题:并发编程Bug的源头中提到了: 多核时代 ...

  6. Java并发(五)线程池使用番外-分析RejectedExecutionException异常

    Java并发(五)线程池使用番外-分析RejectedExecutionException异常 参考文章: (1)Java并发(五)线程池使用番外-分析RejectedExecutionExcepti ...

  7. 在成都Java培训班学习五个多月有用吗?

    不知道"有用"的标准是什么,是能入行上岗工作,还是想只通过几个月的培训一跃成为资深开发攻城狮? 这里不得不给大家泼瓢冷水,短期培训能让你对口上岗工作就很不错了:想要成为技术大佬?大 ...

  8. 20W字纯手打Java并发学习笔记,助力你金三银四,决战春招,必进大厂

    假如阿里给了你这个机会,你却卡在三面,你会不会懊恼? 假如阿里真的让你通过,只需要你把这一块技能的底层原理摸透,你学不学? 我有一个朋友,他小厂背景.15年毕业.普通学校,这看起来确实没什么战斗力,但 ...

  9. 面试经验|春招在即,时间宝贵,这一定是最近的 Java 并发学习路线

    多线程这部分内容确实比较高深而且每个知识点之间比较零散,让人摸不着头脑,不知道该从哪里下手.而且对于大部分学生群体来讲,很少有机会接触到高并发这方面的真实场景,平常自己敲代码也基本不会用到,所以也导致 ...

最新文章

  1. 牛人写SCI常用经典词和常用句型
  2. Java GUI 基础知识2 监听机制
  3. 如何使用Spring Security和Basic身份验证保护Jersey REST服务
  4. T-SQL查询进阶--理解SQL Server中索引的概念,原理以及其他(看了两次了,转了)
  5. android-HttpClient上传信息(包括图片)到服务端
  6. webservice studio 参数是DataSet时不支持中文 解决方法
  7. Python+Django数据库配置及使用——执行原始SQL并返回模型实例
  8. 漫画:什么是图的最小生成树?
  9. 【Interfacenavigation】隐藏导航栏(52)
  10. VARCHART XGantt与活动互动教程指南
  11. zblog小程序模板-青春小程序模板
  12. matlab 双均线,[转载]百年一人的双均线系统及双均线系统公式
  13. win2003服务器性能工具,WindowsServer 2003 Service Pack 1 支持工具
  14. “会说话的汤姆猫家族-时代逐光者”3D数字藏品中奖名单公布
  15. java培训班值得去吗?
  16. 详解怎么更新win10系统操作方法
  17. Spring AOP源码(1)—<aop:config/>AOP配置标签解析【一万字】
  18. 最新 eCharts 世界地图国家名映射
  19. python与spider的区别_python – Scraw spider与Scraped items之间的区别
  20. Warmup 模型训练之标配

热门文章

  1. 【华为OD机试真题 java、python】硬件产品销售方案(100%通过+复盘思路)
  2. 从壹开始前后端分离【 .NET Core2.2 +Vue2.0 】框架之九 || 依赖注入IoC学习 + AOP界面编程初探...
  3. 【Java算法】分治算法
  4. 慧感intersense低功耗NDIR甲烷传感器介绍
  5. English语法_介词 - of
  6. Vayo-Gerber View 层对齐操作方法
  7. as3加载外部图片的方法详解
  8. 2022下半年软考各地报名时间汇总
  9. Android之简单的BMI计算器app
  10. java相关的数学知识_程序员必备的一些数学基础知识