开心一刻

  今天突然收到花呗推送的消息,说下个月 9 号需要还款多少钱

  我就纳了闷了,我很长时间没用花呗了,怎么会欠花呗钱?

  后面我一想,儿子这几天玩了我手机,是不是他偷摸用了我的花呗

  于是我找到儿子问了起来

  我:儿子,你是不是用了我的花呗

  儿子:是的呀,爸,我就用了一点

  我:额度就剩两块了,你用了我用什么?

  儿子:你用你爸的呗!

  我:...

  不对呀,我女朋友都没有,哪里的儿子?猛的被惊醒,大白天的,我特么竟然还做上了白日梦!

前言

  本文是基于 JDK1.8

  那么此时 Java 线程与操作系统线程的对应关系是 1:1 的,有兴趣的可以读一读:深入聊聊java线程模型实现?

  至于 Java 是否在未来引入类似 Go 中的协程,从而实现 Java 线程与操作系统线程的关系是 m:n,那是未来的事,那就未来再说

  我们能确定的是:Java8 中,Java 线程与操作系统线程是 1:1 的

LockSupport 简介

  关于 LockSupport,我们对它感到很陌生,因为我们在工作中很少直接接触到它,但多多少少,我们都间接用到过它

  LockSupport 是 JUC 包下很重要的一个工具类,我们来看看它的源码概述:

    Basic thread blocking primitives for creating locks and other synchronization classes

    用于创建锁和其他同步类的基本线程阻塞原语

  JUC 包下的锁、同步类基本都依赖 LockSupport 实现线程的阻塞与唤醒

  我们可以简单的认为 LockSupport 对 Java 线程(操作系统线程)的阻塞与唤醒进行了封装,简化了开发人员的任务

  permit(许可证)

  LockSupport 的设计思路就是为每一个线程设置一个 permit,其实就是一个值,类似于 AQS 中的 state

  但 permit 没有显示的存在于 LockSupport 的源码中,而 state 却显示的存在于 AQS 的源码中( private volatile int state; )

    permit 默认值(初始值)是 0,permit 最小值是 0,最大值是 1;0 表示许可证不可用,1 表示许可证可用

    若 permit 值为 0,则 park 方法会阻塞当前线程,直至超时或有可用的 permit;若 permit 为 1 ,则 park 方法会将 permit 值设置成 0,不会阻塞当前线程

    不管 permit 的值是 0 还是 1,unpark 方法会将 permit 设置成 1,也就说多次 unpark (中间没有 park)后,permit 的值仍是 1

  那么问题来了,permit 不在 LockSupport 中,那么它在哪?

  其实 permit 体现在 JVM 中,我们来看看在 Hotspot 中对应的源码,在 /hotspot/src/share/vm/runtime/park.hpp 中有如下一段

class Parker : public os::PlatformParker {
private:
volatile int _counter ;
Parker * FreeNext ;
JavaThread * AssociatedWith ; // Current association public:
Parker() : PlatformParker() {
_counter = 0 ;
FreeNext = NULL ;
AssociatedWith = NULL ;
}
protected:
~Parker() { ShouldNotReachHere(); }
public:
// For simplicity of interface with Java, all forms of park (indefinite,
// relative, and absolute) are multiplexed into one call.
void park(bool isAbsolute, jlong time);
void unpark(); // Lifecycle operators
static Parker * Allocate (JavaThread * t) ;
static void Release (Parker * e) ;
private:
static Parker * volatile FreeList ;
static volatile int ListLock ; };

  这个 volatile int _counter 就是 permit 的底层具体实现

LockSupport 核心方法

  方法不多,如下图

  

  主要分两类:park 和 unpark ,我们针对这几个方法,一个一个来看,注意多看注释

  park

  会消耗 permit,若当前没有可用的 permit,则会阻塞当前线程

  park()

    方法体非常简单

    简单的一行: UNSAFE.park(false, 0L); 关于 Unsafe,有兴趣的可以去了解下:Java魔法类:Unsafe应用解析

    只看这个代码,我们很难看出什么,所幸有方法注释,简单翻译一下

    1、除非 permit 可用,否则阻塞当前线程直至 permit 可用

    2、如果 permit 可用,会将 permit 设置成 0,立即返回,不会阻塞当前线程

    3、当 permit 不可用时,当前线程会被阻塞,直至发生以下三种情况

      3.1 其他线程调用 unpark 唤醒此线程

      3.2 其他线程通过 Thread#interrupt 中断此线程

      3.3 该调用不合逻辑地(即毫无理由地)返回,可能是操作系统异常导致的

    4、park() 不会报告是什么原因导致的调用返回,有需要的话,调用者需在返回时自行检查是什么条件导致调用返回

  park(Object blocker)

    方法体也很简单

    功能与 park() 一样,只是多了个入参:Object blocker ,在线程被阻止时记录此对象,以允许监视和诊断工具识别线程被阻止的原因

    我们通过 jstack 命令,来看看 park() 和 park(Object blocker) 线程快照信息有什么区别

    示例代码:

    用 park() 时线程 t1 的快照信息如下

    用 park(Object blocker) 时线程 t1 的快照信息如下

    我们发现 park(Object blocker) 多了一行: - parking to wait for <0x000000076bbb5108> (a java.lang.String)

    当然 park(Object blocker) 不会像示例中那么使用(传个固定的字符串),传的肯定是有意义的对象,我们来看看 JDK 中哪些地方用到了它

    感兴趣的可以去看看具体的代码,其中的 this 具体是什么,它作为 blocker 有什么作用

  parkNanos(long nanos)

    nanos 表示等待的最大纳秒数;我们来翻译一下方法的注释

    1、除非 permit 可用,否则阻塞当前线程直至 permit 可用,或者等待的时间结束

    2、如果 permit 可用,会将 permit 设置成 0,立即返回,不会阻塞当前线程

    3、当 permit 不可用时,当前线程会被阻塞,直至发生以下四种情况

      3.1 其他线程调用 unpark 唤醒此线程

      3.2 其他线程通过 Thread#interrupt 中断此线程

      3.3 经过指定的等待时间,不会无限期的等待下去

      3.4 该调用不合逻辑地(即毫无理由地)返回,可能是操作系统异常导致的

    4、park() 不会报告是什么原因导致的调用返回,有需要的话,调用者需在返回时自行检查是什么条件导致调用返回

    可以看出,功能与 park() 基本一致,只是多了一个等待时长

  parkNanos(Object blocker, long nanos)

    功能与 parkNanos(long nanos) 基本一样,只是多了个 Object blocker

    将 parkNanos(Object blocker, long nanos) 与 parkNanos(long nanos)  的关系与 park(Object blocker) 于 park() 的关系进行类比,就好理解了

    JDK 中有很多地方用到了 parkNanos(Object blocker, long nanos)

  parkUntil(long deadline)

    dealine 表示等待到的绝对时间,以毫秒为单位

    功能与 parkNanos(long nanos) 基本一致,只是 parkNanos(long nanos) 等待的是相对时长(纳秒),而 parkUntil(long deadline) 等待的则是绝对时间点(毫秒)

  parkUntil(Object blocker, long deadline)

    功能与 parkUntil(long deadline),只是多了个 Object blocker

    将 parkUntil(Object blocker, long deadline) 与 parkUntil(long deadline) 的关系与 parkNanos(Object blocker, long nanos) 与 parkNanos(long nanos)  的关系进行列表,就好理解了

    JDK 中有些地方用到了 parkUntil(Object blocker, long deadline)

  unpark

  方法体非常简单

  我们来翻一下它的注释

  1、使入参线程的 permit 可用(将 permit 设置成 1)

  2、如果入参线程正阻塞于 park,那么会唤醒入参线程,否则入参线程的下一次 park 不会阻塞

  3、如果入参线程还没有启动,它不会产生任何效果

  4、如果入参线程为null,它不会产生任何效果

  JDK 中有很多地方用到了它

使用场景

  因为 JDK 已经提供了丰富的 API,所以我们平时基本不会直接使用 LockSupport,所以很多人认为 LockSupport 离我们很远

  其实不然,只要我们用到 JUC 下的类来进行并发编程,那么就已经间接用到了 LockSupport 了

  JUC 中线程的阻塞与唤醒的实现,依赖的都是 LockSupport

  线程交替打印

    这是楼主之前遇到的一个面试题,LockSupport 就是其中的一个考点,具体可查看:记一个有意思的面试题 → 线程交替输出问题

    用 LockSupport 是最优的解决方式,不依赖于第三方的同步值,代码简单,逻辑清晰,非常好理解和实现

总结

  1、park 分三类,每类分两种,官方推荐用带 blocker 参数的那一种

    park()、park(Object blocker)

    parkNanos(long nanos)、parkNanos(Object blocker, long nanos)

    parkUntil(long deadline)、parkUntil(Object blocker, long deadline)

  2、park 与 unpark 之间没有严格的调用先后顺序

    permit = 1 表示可用,permit = 0 表示不可用;permit 属于线程私有

    park 消耗 permit,将 permit 从 1 设置成 0;unpark 则将 permit 设置成 1,不管设置前的值是 1 还是 0

    permit 可用,则 park 不会阻塞当前线程,将 permit 设置成 0,线程继续往下执行,否则 park 会阻塞当前线程

    unpark 会设置指定线程的 permit = 1,并唤醒指定的线程

参考

  Java魔法类:Unsafe应用解析

  JVM 常见线上问题 → CPU 100%、内存泄露 问题排查

Java 并发编程(一) → LockSupport 详解的更多相关文章

  1. Java 并发编程 | 线程池详解

    原文: https://chenmingyu.top/concurrent-threadpool/ 线程池 线程池用来处理异步任务或者并发执行的任务 优点: 重复利用已创建的线程,减少创建和销毁线程造 ...

  2. java 并发编程lock使用详解

    浅谈Synchronized: synchronized是Java的一个关键字,也就是Java语言内置的特性,如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,执行代码块时,其 ...

  3. Java网络编程和NIO详解开篇:Java网络编程基础

    Java网络编程和NIO详解开篇:Java网络编程基础 计算机网络编程基础 转自:https://mp.weixin.qq.com/s/XXMz5uAFSsPdg38bth2jAA 我们是幸运的,因为 ...

  4. Java网络编程和NIO详解8:浅析mmap和Direct Buffer

    Java网络编程与NIO详解8:浅析mmap和Direct Buffer 本系列文章首发于我的个人博客:https://h2pl.github.io/ 欢迎阅览我的CSDN专栏:Java网络编程和NI ...

  5. Java网络编程和NIO详解9:基于NIO的网络编程框架Netty

    Java网络编程和NIO详解9:基于NIO的网络编程框架Netty 转自https://sylvanassun.github.io/2017/11/30/2017-11-30-netty_introd ...

  6. Java网络编程和NIO详解6:Linux epoll实现原理详解

    Java网络编程和NIO详解6:Linux epoll实现原理详解 本系列文章首发于我的个人博客:https://h2pl.github.io/ 欢迎阅览我的CSDN专栏:Java网络编程和NIO h ...

  7. Java网络编程和NIO详解2:JAVA NIO一步步构建IO多路复用的请求模型

    Java网络编程与NIO详解2:JAVA NIO一步步构建IO多路复用的请求模型 知识点 nio 下 I/O 阻塞与非阻塞实现 SocketChannel 介绍 I/O 多路复用的原理 事件选择器与 ...

  8. Java网络编程和NIO详解3:IO模型与Java网络编程模型

    Java网络编程和NIO详解3:IO模型与Java网络编程模型 基本概念说明 用户空间与内核空间 现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32 ...

  9. Java网络编程和NIO详解1:JAVA 中原生的 socket 通信机制

    Java网络编程和NIO详解1:JAVA 中原生的 socket 通信机制 JAVA 中原生的 socket 通信机制 摘要:本文属于原创,欢迎转载,转载请保留出处:https://github.co ...

  10. Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理

    Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理 转自:https://www.jianshu.com/p/2b71ea919d49 本系列文章首发于我的个人博 ...

随机推荐

  1. python之极简ATM系统示例

    """用户可登陆系统输错三次锁定账号用户可以创建新的用户名密码新用户初始账户设为0新用户可直接登陆系统用户登陆成功后可以选择业务类型用户数据可以根据业务修改输入Q随时退出 ...

  2. 极速精简 Go 版 Logstash

    前言 今天来介绍 go-zero 生态的另一个组件 go-stash.这是一个 logstash 的 Go 语言替代版,我们用 go-stash 相比原先的 logstash 节省了2/3的服务器资源 ...

  3. Python爬虫知乎文章,采集新闻60秒

    前言 发现很多人需要新闻的接口,所以自己去搜索了下,发现知乎上正好有对应的用户每天发布新闻简讯,所以自己想写一个新闻的爬虫.如果想做成接口的话,可以加上flask模块即可,这里就暂时只进行爬虫部分的编 ...

  4. 从零玩转SpringSecurity+JWT整合前后端分离

    从零玩转SpringSecurity+JWT整合前后端分离 2021年4月9日 · 预计阅读时间: 50 分钟 一.什么是Jwt? Json web token (JWT), 是为了在网络应用环境间传 ...

  5. MzzTxx——团队介绍

    项目 内容 这个作业属于哪个课程 2021春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 团队项目-团队介绍 我在这个课程的目标是 提升工程能力和团队意识,熟悉软件开发的流程 这个作业在哪 ...

  6. OOUnit4Summary

    一.前三次作业的架构设计 前言 第四单元以uml建模语言为背景,让我们通过已有的模型架构,设计实现类图(顺序图,状态图)解析器:在理解uml整体架构体系的基础上,读懂并熟练运用官方的给的代码,设计有层 ...

  7. (文字版)Qt信号槽源码剖析(三)

    大家好,我是IT文艺男,来自一线大厂的一线程序员 上节视频给大家讲解了Qt信号槽的Qt宏展开推导:今天接着深入分析,进入Qt信号槽源码剖析系列的第三节视频. Qt信号槽宏推导归纳 #define si ...

  8. 让你的Windows/Linux玩上Switch!

    1 前言 某天在Github上面看到了两个Switch的模拟器: yuzu Ryujinx 于是就想动手想尝试一下在Linux上面玩上Switch. 本文首先简单介绍一下两个模拟器,接着是两个模拟器的 ...

  9. JRebel激活

    邮箱随便填,URL为 https://jrebel.qekang.com/ 加上UUID,比如 https://jrebel.qekang.com/2c0c926f-5664-4d0e-afe2-60 ...

  10. element UI el-autocomplete 带输入建议的输入框

    项目需求:需要用户在输入框中输入公司 全名    但是为了避免用户输入不全   需要做一个带输入建议的输入框 参考:https://www.jianshu.com/p/de922caf337c