前言

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. CentOS 7 下网络无法访问 Failed to start LSB: Bring up/.

    [root@localhost Desktop]# ping 192.168.2.1PING 192.168.2.1 (192.168.2.1) 56(84) bytes of data.64 byt ...

  2. Python Flask打造一个视频网站实战视频教程

    下载链接:https://www.yinxiangit.com/607.html 目录: 本套课程从零基础讲解flask开发网站.涉及到的知识点包括:Python和pycharm的安装.urls和视图 ...

  3. linux下创建git代码

    1.创建一个新的repository: 先在github上创建并写好相关名字,描述. $cd ~/hello-world        //到hello-world目录 $git init       ...

  4. java8-Stream原理

    前言 java8新特性目前使用非常广泛,其中Stream更是最常用的特性,这篇文章将介绍Stream的原理,如果你现在还不怎么会用的话可以看一下菜鸟教 https://www.runoob.com/j ...

  5. Golang error 的突围

    目录 error 的困局 尝试破局 Errors are just values handle not just check errors Only handle errors once 小结 胎死腹 ...

  6. Google、百度搜索引擎高级应用6/15

    不得不知道的搜索技巧.

  7. CDH集群安装

    1 软硬件准备 1.1 准备三个节点 序号 主机名 内存 CPU IP地址 角色 1 cdh1 8G 8核心 192.168.5.78 cloudera-scm-server,mysql 2 cdh2 ...

  8. Spring Boot (五): Redis缓存使用姿势盘点

    1. Redis 简介 Redis 是目前业界使用最广泛的内存数据存储.相比 Memcached,Redis 支持更丰富的数据结构,例如 hashes, lists, sets 等,同时支持数据持久化 ...

  9. 【Django】中间件,csrf,缓存,信号

    中间件(middleware) 描述:Middlewares 是修改 Django request 或者 response 对象的钩子. 在django中,中间件其实就是一个类,在请求到来和结束后,d ...

  10. python3在word文档中查找多行文字是否存在

    工作中碰到这样一个情况:有多个关键词存在文本文档txt中,想查找下在某个较大的word文档中,这些关键词是否都含有,没有关键词的显示出来. 因为关键词比较多,并且这个工作还是经常会有的,这个情况我试着 ...