当前位置: 首页 > news >正文

LockSupport简介

LockSupport用来创建锁和其他同步类的基本线程阻塞原语。简而言之,当调用LockSupport.park时,表示当前线程将会等待,直至获得许可,当调用LockSupport.unpark时,必须把等待获得许可的线程作为参数进行传递,好让此线程继续运行。在AQS中大量使用,AQS最终都是使用LockSupport来阻塞线程的。

该类包含一组用于阻塞和唤醒线程的静态方法,这些方法主要是围绕 park 和 unpark 展开,话不多说,直接来看一个简单的例子吧。

java

public class LockSupportDemo1 { public static void main(String[] args) { Thread mainThread = Thread.currentThread(); // 创建一个线程从1数到1000 Thread counterThread = new Thread(() -> { for (int i = 1; i <= 1000; i++) { System.out.println(i); if (i == 500) { // 当数到500时,唤醒主线程 LockSupport.unpark(mainThread); } } }); counterThread.start(); // 主线程调用park LockSupport.park(); System.out.println("Main thread was unparked."); } }

上面的代码中,当 counterThread 数到 500 时,它会唤醒 mainThread。而 mainThread 在调用 park 方法时会被阻塞,直到被 unpark。

LockSupport 中的方法不多,这里将这些方法做一个总结:

阻塞线程

  1. void park():阻塞当前线程,如果调用 unpark 方法或线程被中断,则该线程将变得可运行。请注意,park 不会抛出 InterruptedException,因此线程必须单独检查其中断状态。
  2. void park(Object blocker):功能同方法 1,入参增加一个 Object 对象,用来记录导致线程阻塞的对象,方便问题排查。
  3. void parkNanos(long nanos):阻塞当前线程一定的纳秒时间,或直到被 unpark 调用,或线程被中断。
  4. void parkNanos(Object blocker, long nanos):功能同方法 3,入参增加一个 Object 对象,用来记录导致线程阻塞的对象,方便问题排查。
  5. void parkUntil(long deadline):阻塞当前线程直到某个指定的截止时间(以毫秒为单位),或直到被 unpark 调用,或线程被中断。
  6. void parkUntil(Object blocker, long deadline):功能同方法 5,入参增加一个 Object 对象,用来记录导致线程阻塞的对象,方便问题排查。

唤醒线程

void unpark(Thread thread):唤醒一个由 park 方法阻塞的线程。如果该线程未被阻塞,那么下一次调用 park 时将立即返回。这允许“先发制人”式的唤醒机制。

实际上,LockSupport 阻塞和唤醒线程的功能依赖于sun.misc.Unsafe,这是一个很底层的类,比如 LockSupport 的 park 方法是通过unsafe.park()方法实现的。

LockSupport源码分析

类的属性

java

public class LockSupport { // Hotspot implementation via intrinsics API private static final sun.misc.Unsafe UNSAFE; // 表示内存偏移地址 private static final long parkBlockerOffset; // 表示内存偏移地址 private static final long SEED; // 表示内存偏移地址 private static final long PROBE; // 表示内存偏移地址 private static final long SECONDARY; static { try { // 获取Unsafe实例 UNSAFE = sun.misc.Unsafe.getUnsafe(); // 线程类类型 Class<?> tk = Thread.class; // 获取Thread的parkBlocker字段的内存偏移地址 parkBlockerOffset = UNSAFE.objectFieldOffset (tk.getDeclaredField("parkBlocker")); // 获取Thread的threadLocalRandomSeed字段的内存偏移地址 SEED = UNSAFE.objectFieldOffset (tk.getDeclaredField("threadLocalRandomSeed")); // 获取Thread的threadLocalRandomProbe字段的内存偏移地址 PROBE = UNSAFE.objectFieldOffset (tk.getDeclaredField("threadLocalRandomProbe")); // 获取Thread的threadLocalRandomSecondarySeed字段的内存偏移地址 SECONDARY = UNSAFE.objectFieldOffset (tk.getDeclaredField("threadLocalRandomSecondarySeed")); } catch (Exception ex) { throw new Error(ex); } } }

说明: UNSAFE字段表示sun.misc.Unsafe类,一般程序中不允许直接调用,而long型的表示实例对象相应字段在内存中的偏移地址,可以通过该偏移地址获取或者设置该字段的值。

类的构造函数

java

// 私有构造函数,无法被实例化 private LockSupport() {}

说明: LockSupport只有一个私有构造函数,无法被实例化。

核心函数分析

在分析LockSupport函数之前,先引入sun.misc.Unsafe类中的park和unpark函数,因为LockSupport的核心函数都是基于Unsafe类中定义的park和unpark函数,下面给出两个函数的定义:

java

public native void park(boolean isAbsolute, long time); public native void unpark(Thread thread);

说明: 对两个函数的说明如下:

  • park函数,阻塞线程,并且该线程在下列情况发生之前都会被阻塞:

    • 调用unpark函数,释放该线程的许可。

    • 该线程被中断。

    • 设置的时间到了。并且,当time为绝对时间时,isAbsolute为true,否则,isAbsolute为false。当time为0时,表示无限等待,直到unpark发生。

  • unpark函数,释放线程的许可,即激活调用park后阻塞的线程。这个函数不是安全的,调用这个函数时要确保线程依旧存活。

park函数

park函数有两个重载版本,方法摘要如下

java

public static void park(); public static void park(Object blocker);

说明: 两个函数的区别在于park()函数没有没有blocker,即没有设置线程的parkBlocker字段。park(Object)型函数如下。

java

public static void park(Object blocker) { // 获取当前线程 Thread t = Thread.currentThread(); // 设置Blocker setBlocker(t, blocker); // 获取许可 UNSAFE.park(false, 0L); // 重新可运行后再此设置Blocker setBlocker(t, null); }

说明: 调用park函数时,首先获取当前线程,然后设置当前线程的parkBlocker字段,即调用setBlocker函数,之后调用Unsafe类的park函数,之后再调用setBlocker函数。那么问题来了,为什么要在此park函数中要调用两次setBlocker函数呢? 原因其实很简单,调用park函数时,当前线程首先设置好parkBlocker字段,然后再调用Unsafe的park函数,此后,当前线程就已经阻塞了,等待该线程的unpark函数被调用,所以后面的一个setBlocker函数无法运行,unpark函数被调用,该线程获得许可后,就可以继续运行了,也就运行第二个setBlocker,把该线程的parkBlocker字段设置为null,这样就完成了整个park函数的逻辑。如果没有第二个setBlocker,那么之后没有调用park(Object blocker),而直接调用getBlocker函数,得到的还是前一个park(Object blocker)设置的blocker,显然是不符合逻辑的。总之,必须要保证在park(Object blocker)整个函数执行完后,该线程的parkBlocker字段又恢复为null。所以,park(Object)型函数里必须要调用setBlocker函数两次。setBlocker方法如下。

java

private static void setBlocker(Thread t, Object arg) { // 设置线程t的parkBlocker字段的值为arg UNSAFE.putObject(t, parkBlockerOffset, arg); }

说明: 此方法用于设置线程t的parkBlocker字段的值为arg。

另外一个无参重载版本,park()函数如下。

java

public static void park() { // 获取许可,设置时间为无限长,直到可以获取许可 UNSAFE.park(false, 0L); }

说明: 调用了park函数后,会禁用当前线程,除非许可可用。在以下三种情况之一发生之前,当前线程都将处于休眠状态,即下列情况发生时,当前线程会获取许可,可以继续运行。

  • 其他某个线程将当前线程作为目标调用 unpark。

  • 其他某个线程中断当前线程。

  • 该调用不合逻辑地(即毫无理由地)返回。

parkNanos函数

此函数表示在许可可用前禁用当前线程,并最多等待指定的等待时间。具体函数如下。

java

public static void parkNanos(Object blocker, long nanos) { if (nanos > 0) { // 时间大于0 // 获取当前线程 Thread t = Thread.currentThread(); // 设置Blocker setBlocker(t, blocker); // 获取许可,并设置了时间 UNSAFE.park(false, nanos); // 设置许可 setBlocker(t, null); }
http://www.cnnetsun.cn/news/3055102.html

相关文章:

  • 基于超混沌系统和SHA-256算法的图像加密算法研究算法matlab实现
  • 数学建模一键生成所有图片的实验代码
  • 【滤波】基于平方根无迹卡尔曼滤波SR-UKF实现信号去噪附matlab代码
  • 无特征0day穿透边界防护未触发任何告警 全量行为建模如何4小时锁死全链路影响范围
  • 氢燃料电池(PEMFC)系统仿真建模+空压机、阴极、阳极、电堆模型Matlab仿真
  • AI 前沿速报 | 2026年第27周(6月22日 — 6月28日)
  • 实战指南 | 基于STM32F407 - 利用STM32CubeProgrammer的USB DFU实现无硬件Boot引脚固件升级
  • 高通正面挑战英伟达、华为腾讯百度抢机器人大脑、A股反弹
  • Adobe Illustrator脚本革命:Fillinger智能填充工具的终极指南
  • 意式轻奢高定木作盘点:图森、M77 之外的高性价比之选
  • 【数据融合】千亿体素多维荧光成像结合单像素检测和数据融合附Matlab代码
  • 量子约束优化搜索框架CBQS解析与应用
  • 二分图匈牙利算法KM算法
  • libTomCrypt 轻量级加密库完整教程|编译安装、应用场景、C++ 封装加解密实战代码
  • 大麦抢票协议算法
  • 量化回测【2026.06.29】
  • Ai智能录音笔一机解决各场景录音需求(杰理芯片方案)
  • 哈佛揭开“训练越多越好“的迷思:AI生物推理模型的三阶段炼成法则
  • AMD Radeon Cloud SSH Connection Refused 的原因与解决方案
  • 收藏 | RAG检索实战:关键词+向量+混合+Rerank,小白也能掌握大模型核心技术
  • 深入浅出 Linux 内核・进程篇:ARM 架构
  • TAS2564评估板实战:从数字功放原理到立体声系统集成
  • AcWing算法学习计划
  • 英雄联盟皮肤资源库:一站式获取所有官方皮肤与炫彩包
  • TAS5414A/TAS5424A D类功放诊断与保护机制全解析
  • 分库分表实战
  • SQLModel零基础教程(五)- 工程化封装 迁移工具
  • FluxDown:替代IDM的免费下载器
  • PCB 新手 18 类常见错误汇总
  • OpenGL学习笔记-04-着色器-基础说明