Java读源码之LockSupport
前言
JDK版本: 1.8
作用
LockSupport类主要提供了park和unpark两个native方法,用于阻塞和唤醒线程。注释中有这么一段:
这个类是为拥有更高级别抽象的并发类服务的,开发中我们不会用到这个类
既然只是native方法,开发中也用不到,那么还有必要去看么?
了解LockSupport可以帮助我们更好理解并发,而且大家熟悉的并发中最核心的AQS类中也大量的使用了LockSupport,所以还是有必要看一看的,至少熟悉其中的概念。
为什么需要LockSupport
已经知道了这个类就是阻塞唤醒,Object.wait和Object.notify,Thread.suspend和Thread.resume这两对方法也是类似效果,那么还有必要去看么???
Thread.suspend和Thread.resume为什么被弃用
- suspend将线程挂起,从运行状态阻塞状态,但是并不释放所占用的锁
- suspend方法至少已经满足互斥,不可剥夺两个死锁的条件了
- resume将线程解除挂起,从阻塞状态到运行状态,通常是等待其他任务完成, 请求与保持条件也成立了
- 最后只差 循环等待条件 就死锁了,这实在太危险了,一不小心就容易死锁,而且死锁的问题是很难排查的
Object.wait和Object.notify存在什么问题
- 不满足条件时我们需要在代码中保证拿到锁才能调用,把线程放到等待队列中
- notify是从等待池中随机放一个线程出来,当需要唤醒特定线程时,只能notifyAll
LockSupport会有上面的问题么,又有哪些特点呢,让我们进入源码
源码
类声明和属性
package java.util.concurrent.locks;
public class LockSupport {
// 工具类,ban掉构造
private LockSupport() {}
private static final sun.misc.Unsafe UNSAFE;
// parkBlocker的内存偏移量
private static final long parkBlockerOffset;
private static final long SEED;
private static final long PROBE;
private static final long SECONDARY;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> tk = Thread.class;
// 反射拿到Thread类中的parkBlocker属性,然后获取其在内存中的偏移量
parkBlockerOffset = UNSAFE.objectFieldOffset
(tk.getDeclaredField("parkBlocker"));
SEED = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSeed"));
PROBE = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomProbe"));
SECONDARY = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSecondarySeed"));
} catch (Exception ex) { throw new Error(ex); }
}
}
park()
// 最简单的方式,但是不推荐
public static void park() {
/**
* 将当前线程挂起,是通过二元信号量,获取许可证实现的,拿到许可证后才执行挂起
* 不是基于对象的监视器锁,所以不需要显示的同步
* 如果超时了,被中断了或者unpark了就会return并且释放许可证
* 需要注意的是和wait一样也会因为JVM内部未知原因return,所以我们如果使用也需要放在循环内
* 第一个参数 flase代表纳秒级别超时控制,此级别下第二个参数timeout为0代表无限等待
* 第一个参数 true代表毫秒级别超时控制,此级别下第二个参数timeout为0会立即返回
*/
UNSAFE.park(false, 0L);
}
// 推荐方式,blocker是个辅助对象,用于跟踪许可证的获取,以及定位一些阻塞问题,一般情况park(this)就行
public static void park(Object blocker) {
Thread t = Thread.currentThread();
// 标记对于当前线程t,blocker正在获取许可证,出问题通过getBlocker方法去定位
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
// park操作return了,标记许可证已经释放
setBlocker(t, null);
}
private static void setBlocker(Thread t, Object arg) {
// 通过偏移量,把给当前线程t的parkBlocker属性赋值为arg
UNSAFE.putObject(t, parkBlockerOffset, arg);
}
相信大家已经基本了解park操作了,LockSupport还给我们提供了其他功能
// 推荐,纳秒级别timeout后return
public static void parkNanos(Object blocker, long nanos) {
if (nanos > 0) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, nanos);
setBlocker(t, null);
}
}
// 推荐,毫秒级别timeout后return
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(true, deadline);
setBlocker(t, null);
}
// 不推荐,纳秒级别timeout后return
public static void parkNanos(long nanos) {
if (nanos > 0)
UNSAFE.park(false, nanos);
}
// 不推荐,毫秒级别timeout后return
public static void parkUntil(long deadline) {
UNSAFE.park(true, deadline);
}
unpark
public static void unpark(Thread thread) {
if (thread != null)
//释放线程thread的许可证,如果已经是释放状态那就什么都不会发生,因为总共就1个许可,所以unpark可以先于park执行没有任务问题
UNSAFE.unpark(thread);
}
其他方法
// 由于包权限问题从ThreadLocalRandom类中copy过来的,用于生成随机数种子
static final int nextSecondarySeed() {
int r;
Thread t = Thread.currentThread();
if ((r = UNSAFE.getInt(t, SECONDARY)) != 0) {
r ^= r << 13; // xorshift
r ^= r >>> 17;
r ^= r << 5;
}
else if ((r = java.util.concurrent.ThreadLocalRandom.current().nextInt()) == 0)
r = 1; // avoid zero
UNSAFE.putInt(t, SECONDARY, r);
return r;
}
实践
public class LockSupportTest {
public static void main(String[] args) {
AtomicBoolean flag = new AtomicBoolean(true);
Thread thread = new Thread(() -> {
Thread curr = Thread.currentThread();
System.out.println("线程1 即将被阻塞");
while (flag.get()) {
LockSupport.park(curr);
System.out.println("线程1 复活");
}
System.out.println("线程1 结束使命");
});
thread.start();
new Thread(() -> {
System.out.println("唤醒线程1");
flag.compareAndSet(true, false);
LockSupport.unpark(thread);
}).start();
}
/**
* 输出:
* 线程1 即将被阻塞
* 唤醒线程1
* 线程1 复活
* 线程1 结束使命
*/
}
Java读源码之LockSupport的更多相关文章
- Java读源码之ReentrantLock
前言 ReentrantLock 可重入锁,应该是除了 synchronized 关键字外用的最多的线程同步手段了,虽然JVM维护者疯狂优化 synchronized 使其已经拥有了很好的性能.但 R ...
- Java读源码之ReentrantLock(2)
前言 本文是 ReentrantLock 源码的第二篇,第一篇主要介绍了公平锁非公平锁正常的加锁解锁流程,虽然表达能力有限不知道有没有讲清楚,本着不太监的原则,本文填补下第一篇中挖的坑. Java读源 ...
- Java读源码之CountDownLatch
前言 相信大家都挺熟悉 CountDownLatch 的,顾名思义就是一个栅栏,其主要作用是多线程环境下,让多个线程在栅栏门口等待,所有线程到齐后,栅栏打开程序继续执行. 案例 用一个最简单的案例引出 ...
- Java读源码之Thread
前言 JDK版本:1.8 阅读了Object的源码,wait和notify方法与线程联系紧密,而且多线程已经是必备知识,那保持习惯,就从多线程的源头Thread类开始读起吧.由于该类比较长,只读重要部 ...
- Java读源码之ThreadLocal
前言 JDK版本: 1.8 之前在看Thread源码时候看到这么一个属性 ThreadLocal.ThreadLocalMap threadLocals = null; ThreadLocal实现的是 ...
- Java读源码之Object
前言 JDK版本: 1.8 最近想看看jdk源码提高下技术深度(比较闲),万物皆对象,虽然Object大多native方法但还是很重要的. 源码 package java.lang; /** * Ja ...
- java读源码 之 map源码分析(HashMap,图解)一
开篇之前,先说几句题外话,写博客也一年多了,一直没找到一种好的输出方式,博客质量其实也不高,很多时候都是赶着写出来的,最近也思考了很多,以后的博客也会更注重质量,同时也尽量写的不那么生硬,能让大家 ...
- java读源码 之 queue源码分析(PriorityQueue,附图)
今天要介绍的是基础容器类(为了与并发容器类区分开来而命名的名字)中的另一个成员--PriorityQueue,它的大名叫做优先级队列,想必即使没有用过也该有所耳闻吧,什么?没..没听过?emmm... ...
- java读源码 之 list源码分析(LinkedList)
文章目录 LinkedList: 继承关系分析: 字段分析: 构造函数分析: 方法分析: LinkedList: 继承关系分析: public class LinkedList<E> ex ...
随机推荐
- 【Ehcache】基础知识学习
一.Ehcache概述 1.1 简介 1.2 Ehcache的主要特性 二.Ehcache使用介绍 2.1 Ehcache缓存过期策略 2.2 如何解决缓存与db不同步的问题. 三.Ehcache 基 ...
- dropwizard-core模块和应用启动分析
简介 Dropwizard是一款开发运维友好.高效.RESTful web服务的框架.Dropwizard将稳定.成熟的java生态系统中的库整合为一个简单的.轻量级的包,即跨越了库和框架之间的界限, ...
- Could not calculate build plan :lugin org.apache.maven.plugins:maven-resources-plugin:2.6 or one of
eclipse中新建maven项目,出现 Could not calculate build plan :lugin org.apache.maven.plugins:maven-resources- ...
- android Action中的data属性
(2) 根据Action和Data匹配 <activity android:name=".MyActivityTwo" android:label="@string ...
- FreeSql (一)入门
FreeSql 是一个功能强大的对象关系映射程序(O/RM),支持 .NETCore 2.1+ 或 .NETFramework 4.5+(QQ群:4336577) FreeSql采用MIT开源协议托管 ...
- CentOS7 自定义登录前后欢迎信息
博客地址:http://www.moonxy.com 一.摘要 本人当前使用的是阿里云 ECS 服务器,操作系统为 linux,发行版为 CentOS 7.4.1708.系统默认都已经提供了欢迎信息, ...
- Senparc.Weixin.MP SDK 微信公众平台开发教程(二十二):如何安装 Nuget(dll) 后使用项目源代码调试
最近碰到开发者问:我使用 nuget 安装了 Senparc.Weixin SDK,但是有一些已经封装好的过程想要调试,我又不想直接附加源代码项目,这样就没有办法同步更新了,我应该怎么办? 这其实是一 ...
- 控制执行流程之switch语句
switch语句,同时也是一种选择性语句,其根据整数表达式的值(整数选择因子)选择一段代码去执行.整数选择因子为int类型或者char类型.
- Java位运算符&、|、^、>>、<<、~、>>>
如果要搞懂Java中的位运算符,首先要搞懂二进制的运算,之前一篇有介绍详细请看 二进制运算-十进制与二进制的转换 Java中的位运算符有:&(按位与).|(按位或).^(按位异或).>& ...
- 安装MariaDB
1.安装MariaDB安装命令yum -y install mariadb mariadb-server安装完成MariaDB,首先启动MariaDBsystemctl start mariadb设置 ...