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 ...
随机推荐
- Elasticsearch在Java中的增删改查
public class ElasticAPI { private static RestClient restClient; static { restClient=RestClient.build ...
- 获得本机IP和访问服务的端口号(Java)
1. //获取本机ip地址 InetAddress addr = InetAddress.getLocalHost(); String ip=addr.getHostAddress().toStrin ...
- Python(Head First)学习笔记:六
6 定制数据对象:数据结构自定义 打包代码与数据 james2.txt: James Lee,2002-3-14,2-34,3:21,2.34,2.45,3.01,2:01,2:01,3:10,2-2 ...
- windows下安装spark
1.安装jdk 2.安装scala 3.下载spark spark下载地址 3.1安装spark 将下载的文件解压到一个目录,注意目录不能有空格,比如说不能解压到C:\Program Files 作者 ...
- Hadoop入门 之 Hadoop常识
1.Hadoop是什么? 答:Hadoop是开源的分布式存储和分布式计算平台. 2.Hadoop的组成是什么? 答:Hadoop由HDFS和MapReduce这两个核心部分组成. HDFS(Hadoo ...
- Anaconda、TensorFlow安装和Pycharm配置详细教程,亲测有效!
目录 1.Anaconda下载与安装 2.Anaconda安装成功与否测试 3.安装python 4.检查TensorFlow环境添加成功与否 5.TensorFlow安装 6.测试TensorFlo ...
- 【linux】【FastDFS】FastDFS数据迁移
后来同步的时候发现有的没有同步过来,应该是没有同步完成我就停止服务了. 最后尝试直接把fastdfs storage的data文件迁移过去即可. 1.在新的storage server服务器上停止所有 ...
- Go微服务容错与韧性(Service Resilience)
Service Resilience是指当服务的的运行环境出现了问题,例如网络故障或服务过载或某些微服务宕机的情况下,程序仍能够提供部分或大部分服务,这时我们就说服务的韧性很强.它是微服务中很重要的一 ...
- Java基础之final、static关键字
一.前言 关于这两个关键字,应该是在开发工作中比较常见的,使用频率上来说也比较高.接口中.常量.静态方法等等.但是,使用频繁却不代表一定是能够清晰明白的了解,能说出个子丑演卯来.下面,对这两个关键字的 ...
- kvm-web管理工具webvirtmgr
前言: 使用开源的虚拟化技术,对公司自有的少数服务器进行虚拟化,在满足业务需求的同时,并未增加投入,同时也避免了使用云主机的不便,技术层面,kvm是开源的虚拟化产品,虚拟化性能出众,更重要的是免费!! ...