文前说明
作为码农中的一员, 需要不断的学习, 我工作之余将一些分析总结和学习笔记写成博客与大家一起交流, 也希望采用这种方式记录自己的学习之旅.
本文仅供学习交流使用, 侵权必删.
1. LockSupport
LockSupport 是用来创建锁和其他同步类的基本线程阻塞原语.
是一个简单的代理类, 里面的代码都是使用 Unsafe 类里面的方法.
JDK 对 LockSupport 的描述: Basic thread blocking primitives for creating locks and other synchronization classes.
在 Java 多线程中, 当需要阻塞或者唤醒一个线程时, 都会使用 LockSupport 工具类来完成相应的工作.
LockSupport 定义了一组公共静态方法, 这些方法提供了最基本的线程阻塞和唤醒功能, 而 LockSupport 也因此成为了构建同步组件的基础工具.
LockSupport 中的 park() 和 unpark() 的作用分别是阻塞线程和解除阻塞线程, 而且 park() 和 unpark() 不会遇到 "Thread.suspend" 和 "Thread.resume" 所可能引发的 死锁 问题.
因为 park() 和 unpark() 有许可的存在.
调用 park() 的线程和另一个试图将其 unpark() 的线程之间的竞争将保持活性.
1.1 LockSupport 函数列表
- // 返回提供给最近一次尚未解除阻塞的 park 方法调用的 blocker 对象, 如果该调用不受阻塞, 则返回 null.
- static Object getBlocker(Thread t)
- // 为了线程调度, 禁用当前线程, 除非许可可用.
- static void park()
- // 为了线程调度, 在许可可用之前禁用当前线程.
- static void park(Object blocker)
- // 为了线程调度禁用当前线程, 最多等待指定的等待时间, 除非许可可用.
- static void parkNanos(long nanos)
- // 为了线程调度, 在许可可用前禁用当前线程, 并最多等待指定的等待时间.
- static void parkNanos(Object blocker, long nanos)
- // 为了线程调度, 在指定的时限前禁用当前线程, 除非许可可用.
- static void parkUntil(long deadline)
- // 为了线程调度, 在指定的时限前禁用当前线程, 除非许可可用.
- static void parkUntil(Object blocker, long deadline)
- // 如果给定线程的许可尚不可用, 则使其可用.
- static void unpark(Thread thread)
LockSupport 的核心方法是基于 Unsafe 类中的 park() 和 unpark() 方法.
变量 isAbsolute 代表传入的 time 是绝对时间还是相对时间.
unpark 函数为线程提供 "许可(permit)", 线程调用 park() 函数则等待 "许可", 有点像信号量, 但是这个 "许可" 是不能叠加的,"许可" 是一次性的. 可以理解为设置一个变量 0,1 之间的切换.
如果线程 B 连续调用了多次 unpark() 函数, 当线程 A 调用 park() 函数就使用了这个 "许可", 如果线程 A 第二次调用 park(), 则进入等待状态.
unpark() 函数可以先于 park() 调用.
如线程 B 调用 unpark() 函数, 给线程 A 一个 "许可", 那么当线程 A 调用 park() 时, 发现已经有 "许可", 可以马上继续运行, 不会阻塞.
- // 阻塞线程
- public native void park(boolean isAbsolute, long time);
- // 取消阻塞线程
- public native void unpark(Object thread);
调用了 park() 方法后, 会禁用当前线程, 以下几种情况时, 线程会被唤醒.
其他某个线程将当前线程作为目标调用 unpark().(调用 unpark 方法)
其他某个线程中断当前线程.(被中断 interrupts)
该调用不合逻辑地 (即毫无理由地) 返回.(posix condition 里的 "Spurious wakeup")
- public static void park(Object blocker) {
- // 获取当前线程
- Thread t = Thread.currentThread();
- // 设置线程的 blocker 对象
- setBlocker(t, blocker);
- // 通过 UNSAFE 调用, 挂起线程
- UNSAFE.park(false, 0L);
- // 挂起的线程被唤醒以后, 需要将阻塞的 Blocker 清理掉.
- setBlocker(t, null);
- }
获取当前线程, 设置当前线程的 parkBlocker 字段, 调用 Unsafe 类的 park() 方法, 再次调用 setBlocker 的原因.
Unsafe 的 park() 方法之后, 当前线程已被阻塞.
unpark() 方法被调用, 该线程获得许可后, 继续进行下面的代码, setBlocker() 参数 parkBlocker 字段设置为 null, 这样就完成了整个 park() 方法的逻辑.
setBlocker 修改的是 parkBlockerOffset 变量.
这个变量是挂起线程对象的偏移地址, 对应的是 Thread 类的 parkBlocker.
这个对象是用于记录线程被阻塞时被谁阻塞, 用于线程监控和分析工具定位原因.
parkBlocker 是在线程处于阻塞的情况下才被赋值. 线程已经被阻塞, 只能通过设置偏移量这种修改内存的方法来进行修改, 如果调用线程内的方法, 线程是不会回应调用的.
- Class<?> tk = Thread.class;
- parkBlockerOffset = UNSAFE.objectFieldOffset(tk.getDeclaredField("parkBlocker"));
阻塞当前线程, 最长等待时间不超过 nanos 毫秒, 同样, 在阻塞当前线程的时候做了记录当前线程等待的对象操作.
- 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);
- }
- }
阻塞当前线程直到 deadline 时间, 相同的, 也做了阻塞前记录当前线程等待对象的操作.
- public static void parkUntil(Object blocker, long deadline) {
- Thread t = Thread.currentThread();
- setBlocker(t, blocker);
- UNSAFE.park(true, deadline);
- setBlocker(t, null);
- }
线程在 park() 上受阻塞, 将解除其阻塞状态.
否则, 预发许可, 下一次调用 park() 不会受阻塞.
- public static void unpark(Thread thread) {
- if (thread != null)
- UNSAFE.unpark(thread);
- }
1.2 与 wait(),notify() 的区别
面向的主体不一样.
LockSupport 主要是针对 Thread 进进行阻塞处理, 可以指定阻塞队列的目标对象, 每次可以指定具体的线程唤醒.
Object.wait() 是以对象为纬度, 阻塞当前的线程和唤醒单个 (随机) 或者所有线程.
实现机制不同.
虽然 LockSupport 可以指定 Monitor 的 object 对象, 但和 object.wait() 两者的阻塞队列并不交叉.
LockSupport 阻塞和解除阻塞线程直接操作的是 Thread. 而 Object 的 wait/notify 并不是直接对线程操作, 是被动的方法, 需要一个 Object 来进行线程的挂起或唤醒.
Thead 在调用 wait 之前, 当前线程必须先获得该对象的监视器(syschronized), 被唤醒之后需要重新获取到监视器才能继续执行. 而 LockSupport 可以随意进行 park 或者 unpark.
来源: http://www.jianshu.com/p/2a79c115464c