前言

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的更多相关文章

  1. Java读源码之ReentrantLock

    前言 ReentrantLock 可重入锁,应该是除了 synchronized 关键字外用的最多的线程同步手段了,虽然JVM维护者疯狂优化 synchronized 使其已经拥有了很好的性能.但 R ...

  2. Java读源码之ReentrantLock(2)

    前言 本文是 ReentrantLock 源码的第二篇,第一篇主要介绍了公平锁非公平锁正常的加锁解锁流程,虽然表达能力有限不知道有没有讲清楚,本着不太监的原则,本文填补下第一篇中挖的坑. Java读源 ...

  3. Java读源码之CountDownLatch

    前言 相信大家都挺熟悉 CountDownLatch 的,顾名思义就是一个栅栏,其主要作用是多线程环境下,让多个线程在栅栏门口等待,所有线程到齐后,栅栏打开程序继续执行. 案例 用一个最简单的案例引出 ...

  4. Java读源码之Thread

    前言 JDK版本:1.8 阅读了Object的源码,wait和notify方法与线程联系紧密,而且多线程已经是必备知识,那保持习惯,就从多线程的源头Thread类开始读起吧.由于该类比较长,只读重要部 ...

  5. Java读源码之ThreadLocal

    前言 JDK版本: 1.8 之前在看Thread源码时候看到这么一个属性 ThreadLocal.ThreadLocalMap threadLocals = null; ThreadLocal实现的是 ...

  6. Java读源码之Object

    前言 JDK版本: 1.8 最近想看看jdk源码提高下技术深度(比较闲),万物皆对象,虽然Object大多native方法但还是很重要的. 源码 package java.lang; /** * Ja ...

  7. java读源码 之 map源码分析(HashMap,图解)一

    ​ 开篇之前,先说几句题外话,写博客也一年多了,一直没找到一种好的输出方式,博客质量其实也不高,很多时候都是赶着写出来的,最近也思考了很多,以后的博客也会更注重质量,同时也尽量写的不那么生硬,能让大家 ...

  8. java读源码 之 queue源码分析(PriorityQueue,附图)

    今天要介绍的是基础容器类(为了与并发容器类区分开来而命名的名字)中的另一个成员--PriorityQueue,它的大名叫做优先级队列,想必即使没有用过也该有所耳闻吧,什么?没..没听过?emmm... ...

  9. java读源码 之 list源码分析(LinkedList)

    文章目录 LinkedList: 继承关系分析: 字段分析: 构造函数分析: 方法分析: LinkedList: 继承关系分析: public class LinkedList<E> ex ...

随机推荐

  1. Day004课程内容

    本节主要内容: 1.列表List L = [1,'哈哈哈','吼吼',[1,8,0],('"我“,"叫","元","组"),”ab ...

  2. protobuf初识

    protobuf是一种高效的数据格式,平台无关.语言无关.可扩展,可用于 RPC 系统和持续数据存储系统. protobuf protobuf介绍 Protobuf是Protocol Buffer的简 ...

  3. 集合数组与String的互转

    1.集合转成数组: 转之前集合里面存的什么类型的数据,就new什么类(特别:存的是基本数据的封装类,就要new他的封装类) 例如: 1.1集合: ArrayList<Character> ...

  4. .Ajax(async异步与sync同步)

    异步,不会阻碍代码的执行,它会等待所有的同步代码执行完毕后,再执行输出自己的同步结果.(原生js中,只有定时器,DOM,ajax三个东西是异步的.) 同步,代码只会从上到下依次执行,只要一步出错,接下 ...

  5. 在 ABP vNext 中编写仓储单元测试的问题一则

    一.问题 新项目是基于 ABP vNext 框架进行开发的,所以我要求为每层编写单元测试.在同事为某个仓储编写单元测试的时候,发现了一个奇怪的问题.他的对某个聚合根的 A 字段进行了更新,随后对某个导 ...

  6. ACM团队招新赛题解

    标程代码全部为C语言编写.代码中的#if LOCAL_ 至#endif为本地一些调试内容,可以忽略. Xenny的A+B(1)[容易][签到] 签到题,做不出的话可能你有点不太适合ACM了. Xenn ...

  7. Asp.Net Core2.2 源码阅读系列——控制台日志源码解析

      为了让我们第一时间知道程序的运行状态,Asp.Net Core 添加了默认的日志输出服务.这看起来并没有什么问题,对于开发人员也相当友好,但如果不了解日志输出的细节,也有可能因为错误的日志级别配置 ...

  8. [Leetcode] 第331题 验证二叉树的前序序列化

    一.题目描述 序列化二叉树的一种方法是使用前序遍历.当我们遇到一个非空节点时,我们可以记录下这个节点的值.如果它是一个空节点,我们可以使用一个标记值记录,例如 #. _9_ / \ 3 2 / \ / ...

  9. SoapUI接口测试实战

    本次测试的是REST服务,使用的SoapUI破解版,如果担心开源版的会有功能差异,可以参照我之前的博文安装破解版. 博文地址:https://www.cnblogs.com/Sweettesting/ ...

  10. 使用System.Text.Json处理Json文档以及部分坑

    System.Text.Json处理Json文档需要用到JsonDocument,JsonElement,JsonProperty. JsonDocument就是一个表示Json文档的东西,JsonE ...