LockSupport实现原理

LockSupport是Java中用于线程阻塞和唤醒的基础工具,它通过park和unpark方法实现。线程在调用park时,如果许可证可用则继续执行,否则会被阻塞。unpark方法可以唤醒被阻塞的线程。内部使用Unsafe类直接操作内存来设置和获取线程的parkBlocker,从而控制线程状态。Unsafe的park和unpark方法涉及到了线程的Parker对象,用于实际的阻塞和解除阻塞操作。

文中了解到AQS借助LockSupport.park和LockSupport.unpark完成线程的阻塞和唤醒,那么LockSupport内部又是怎么实现的?这是一个什么类?

LockSupport是用于使用锁阻塞线程的基础实现,是其他同步类的基础,这个类为每个使用它的线程关联一个许可证(有点类似于Semaphore),如果许可证可用,线程调用park方法时会立即返回,线程正常执行,否则当前线程阻塞,直到有其他线程调用unpark使得许可证可用,此时线程被唤醒,再次尝试获取许可证,其内部定义的park和unpark方法提供了阻塞和解决阻塞的基本实现。

LockSupport常见函数如下表所示:

函数名称说明备注
void park()阻塞当前线程在下列情况发生时唤醒: 1.调用unpark函数,释放该线程的许可; 2.该线程被中断; 3.设置的阻塞超时时间耗尽; 4.到达设置的指定时间
void park(Object blocker)使用指定的blocker对象阻塞当前线程唤醒条件,park中已说明
void parkNanos(long nanos)阻塞当前线程直到超时时间耗尽,nanos为指定的超时时间唤醒条件,park中已说明
void parkNanos(Object blocker, long nanos)在超时时间耗尽前,使用指定的blocker对象阻塞当前线程,如果在到达超时时间后,许可仍不可用,则结束阻塞唤醒条件,park中已说明
void parkUntil(long deadline)在指定时间前,阻塞该线程,如果在到达指定时间后,许可仍不可用,则结束阻塞唤醒条件,park中已说明
void parkUntil(Object blocker, long deadline)在指定时间前,使用指定的对象阻塞该线程,如果在到达指定时间后,许可仍不可用,则结束阻塞唤醒条件,park中已说明
void unpark(Thread thread)用于唤醒传入的在阻塞中的线程/
Object getBlocker(Thread t)获取当前线程的阻塞对象/
void setBlocker(Thread t, Object arg)使用指定对象为线程设置阻塞对象/

LockSupport.park

ReentrantLock中调用LockSupport.park代码如下所示:

 // AbstractQueuedSynchronizer.java
 private final boolean parkAndCheckInterrupt() {
     LockSupport.park(this);
     return Thread.interrupted();
 }
复制代码

在LockSupport中,void park(Object blocker)实现代码如下:

 // LockSupport.java
 public static void park(Object blocker) {
     Thread t = Thread.currentThread();
     setBlocker(t, blocker);
     UNSAFE.park(false, 0L);
     setBlocker(t, null);
 }
复制代码

可以看到在park流程中主要包含以下过程:

  1. 获取当前线程

  2. 将传入的对象设置为该线程的parkBlocker

    setBlocker函数实现如下所示:

     private static void setBlocker(Thread t, Object arg) {
         // Even though volatile, hotspot doesn't need a write barrier here.
         UNSAFE.putObject(t, parkBlockerOffset, arg);
     }
    复制代码

    可以看到这里新出现了UNSAFE和parkBlockerOffset两个标识,这两个是用来干嘛的?我们一起看看其声明的代码:

     private static final sun.misc.Unsafe UNSAFE;
     private static final long parkBlockerOffset;
    复制代码
     static {
         try {
             UNSAFE = sun.misc.Unsafe.getUnsafe();
             Class<?> tk = Thread.class;
             parkBlockerOffset = UNSAFE.objectFieldOffset
                 (tk.getDeclaredField("parkBlocker"));
             .....
         } catch (Exception ex) { throw new Error(ex); }
     }
    复制代码

    可以看到UNSAFE对象是通过Unsafe.getUnsafe()获取的,那么Unsafe这个类到底是干嘛的?

    大家都知道Java对象在内存中创建,大多数情况下我们都是通过类的对象去修改和访问内存中的数据的,那么如果需要直接从内存修改某一对象的取值,应该怎么做呢?就是使用Unsafe类,该类只允许在JDK信任的类中调用(当前也可以用反射实例化该类对象)。

    在Unsafe类中定义了两个重要函数park和unpark,其中park用于实现线程阻塞,unpark用于实现线程唤醒(Unsafe本质上是操作线程的Parker对象来完成线程阻塞和唤醒的,具体见参考链接,了解即可),上文中的parkBlockerOffset正是定义了Thread类的parkBlocker属性成员的内存偏移量,使用该值再结合Unsafe对象就可以实现直接操作内存中的parkBlocker值的目的,Thread类中的parkBlocker声明如下:

     // Thread.java
     volatile Object parkBlocker;
    复制代码

    可以得到这一环节主要是将AQS作为blocker设置到当前线程的parkBlocker成员属性上。

    CAS底层也是通过Unsafe执行的

  3. 执行UNSAFE.park

    结合上文可知,这步完成后,当前线程阻塞

  4. 设置线程的parkBlocker为null

    第三步中线程处于阻塞状态,当然就不能执行设置parkBlocker为null的操作了,那么什么时候执行呢?当线程从阻塞状态唤醒时,执行该步骤,使得线程的parkBlocker对象恢复初始状态。

LockSupport.unpark

LockSupport.unpark代码如下所示:

 // LockSupport.java
 public static void unpark(Thread thread) {
     if (thread != null)
         UNSAFE.unpark(thread);
 }
复制代码

可以看出当传入的线程不为空时,执行Unsafe的park函数唤醒当前线程,取消阻塞,此时继续执行park函数中的setBlocker(null),将parkBlocker成员设置为null。

作者:小海编码日记
原文链接:https://juejin.cn/post/7186296167406731321
 

<think>好的,我现在需要详细回答用户关于JavaLockSupport实现原理的问题。用户提到了站内引用中的两篇解析文章,我需要结合这些内容,确保回答准确。 首先,LockSupportJava并发包中的基础工具,主要提供线程的阻塞和唤醒功能。核心方法是park和unpark。根据引用[^1],LockSupport实现与CAS、volatile以及底层操作系统相关,比如在Linux下可能使用pthread mutex和condition变量。 接下来,我需要解析park和unpark的源码。每个线程有一个许可(permit),park会消耗许可,unpark会提供许可。许可的存在避免了线程挂起,似信号量。要注意的是,unpark可以在park之前调用,此时park不会阻塞。 然后,要说明LockSupport与Object.wait()/notify()的区别。LockSupport不需要在同步块中使用,且unpark可以指定线程,更加灵活。这部分需要引用中的比较。 还需要提到Unsafe的使用,因为LockSupport内部调用的是Unsafe的native方法。这些方法最终会调用操作系统的线程管理函数,比如pthread_cond_wait。 此外,结合引用[^2]中关于线程池的实现,说明LockSupport在JUC中的应用,比如AQS中等待队列的管理。线程池中的工作线程在任务队列为空时可能会被park,直到有新任务时被unpark。 最后,确保回答结构清晰,分点说明原理、源码、与其它机制的区别以及应用场景。同时生成相关问题,如与synchronized的区别、许可机制的具体实现等。</think>### Java LockSupport 实现原理与源码解析 #### 1. LockSupport 的核心作用 LockSupportJava 并发编程的基础工具,提供线程阻塞(`park`)和唤醒(`unpark`)的核心能力。它直接与 JVM 和操作系统交互,是 `ReentrantLock`、`AQS` 等并发组件的底层实现基础[^1]。 --- #### 2. 核心方法:`park` 与 `unpark` - **`park()`** 阻塞当前线程,直到满足以下条件之一: - 其他线程调用此线程的 `unpark`。 - 线程被中断(`Thread.interrupt()`)。 - 调用逻辑存在竞争条件(如虚假唤醒)。 - **`unpark(Thread thread)`** 显式唤醒指定线程。与 `notify()` 不同,`unpark` 不需要事先获取锁,且可作用于任意线程。 --- #### 3. 源码解析(基于 JDK 1.8) ##### 关键数据结构 每个线程通过 `Unsafe` 关联一个**许可(permit)**,初始值为 0: - `unpark`:将许可设为 1(若原本为 0)。 - `park`:若许可为 1 则消耗许可并立即返回;否则阻塞。 ##### 核心代码逻辑 ```java // LockSupport 中的 Unsafe 实例 private static final Unsafe UNSAFE = Unsafe.getUnsafe(); public static void park() { UNSAFE.park(false, 0L); } public static void unpark(Thread thread) { if (thread != null) { UNSAFE.unpark(thread); } } ``` ##### 底层实现(以 Linux 为例) - **`Unsafe.park`** 最终调用 `pthread_cond_wait`,结合互斥锁实现条件等待。 - **`Unsafe.unpark`** 调用 `pthread_cond_signal` 唤醒线程。 --- #### 4. 与 `synchronized` 的区别 | 特性 | LockSupport | `synchronized` | |--------------------|-----------------------------|------------------------------| | 锁机制 | 显式许可机制(CAS) | 内置监视器锁 | | 唤醒精准性 | 可指定线程 | 随机唤醒(`notify()`) | | 调用条件 | 无需持有锁 | 必须在同步块内调用 | | 中断响应 | 支持中断后直接返回 | 抛出 `InterruptedException` | --- #### 5. 应用场景 - **AQS(AbstractQueuedSynchronizer)**:用于实现线程的等待队列(如 `ReentrantLock`)。 - **线程池**:任务队列空时,工作线程通过 `park` 阻塞,新任务到达时 `unpark` 唤醒。 - **定时任务**:`ScheduledThreadPoolExecutor` 使用 `LockSupport.parkNanos` 实现延迟。 --- #### 6. 关键设计思想 - **无锁化设计**:通过 `Unsafe` 直接操作内存,避免锁竞争。 - **线程局部性**:许可与线程绑定,确保唤醒的精准性。 - **与 JVM 协作**:JVM 在垃圾回收等操作中会自动处理线程阻塞状态。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值