LockSupport
定义:线程阻塞的工具类,用于线程阻塞与释放;通常作为锁实现的工具类;其中主要方法为park()、unpark(),实现借助Unsafe;
park-意为等待许可
unpark-意为许可
数据结构
-
私有构造
private LockSupport() {} // Cannot be instantiated. -
属性
// Hotspot implementation via intrinsics API // UNSAFE实例 private static final sun.misc.Unsafe UNSAFE; // 用于记录线程被谁阻塞的 // 获取Thread类中属性的偏移量 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; 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); } }
上文提到了许可"permit",现在对其稍作解释:
LockSupport与每一个使用他的线程都与一个permit相关联,其最多有一个。
permit相当于线程是否阻塞、释放的开关,默认值为0(1,0):
- LockSupport.unpark() - permit+=1, 即permit = 1
- LockSupport.park() - permit -= 1,即permit = 0
- 再次调用LockSupport.park() , 则线程阻塞: 因为permit的值为0,它需要等待LockSupport.unpark() 调用,否则就一直被阻塞直到permit值为1
LockSupport的常用方法
-
set/getBlocker
private static void setBlocker(Thread t, Object arg) { // Even though volatile, hotspot doesn't need a write barrier here. UNSAFE.putObject(t, parkBlockerOffset, arg); } /** * Returns the blocker object supplied to the most recent * invocation of a park method that has not yet unblocked, or null * if not blocked. The value returned is just a momentary * snapshot -- the thread may have since unblocked or blocked on a * different blocker object. * * @param t the thread * @return the blocker * @throws NullPointerException if argument is null * @since 1.6 * 获取最近一次尚未解除阻塞的被park方法调用的blocker对象,如果该调用未阻塞,则返回null */ public static Object getBlocker(Thread t) { if (t == null) throw new NullPointerException(); return UNSAFE.getObjectVolatile(t, parkBlockerOffset); } -
park
作用:
等待许可(即阻塞),调用时会发生两种情况:
- 当许可可用时,消耗许可并返回
- 当许可不可用时,线程阻塞等待许可
// public static void park() { UNSAFE.park(false, 0L); } public static void park(Object blocker) { Thread t = Thread.currentThread(); setBlocker(t, blocker); UNSAFE.park(false, 0L); setBlocker(t, null); } // 纳秒-超时返回 public static void parkNanos(long nanos) { if (nanos > 0) UNSAFE.park(false, 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); } } // 毫秒级-直到这个时间返回 public static void parkUntil(long deadline) { UNSAFE.park(true, deadline); } public static void parkUntil(Object blocker, long deadline) { Thread t = Thread.currentThread(); setBlocker(t, blocker); UNSAFE.park(true, deadline); setBlocker(t, null); } -
unpark
作用:
获取许可-释放锁;
- 如果许可不可用,则变为可用,释放锁
- 如果许可可用,调用LockSupport.park()时则不会阻塞
// 释放(线程)其许可以及锁 public static void unpark(Thread thread) { if (thread != null) UNSAFE.unpark(thread); }
更多
LockSupport不可重入
LockSupport.unpark(Thread.currentThread());
System.out.println("执行unpark");
LockSupport.park();
System.out.println("执行第一次park");
LockSupport.park();
System.out.println("执行第二次park");
// sout = 执行unpark \n 执行第一次park
LockSupport可中断
中断响应: 线程在park阻塞状态下仍然能够被中断且不抛出异常
Thread thread = new Thread(() -> {
long start = System.currentTimeMillis();
while ((System.currentTimeMillis() - start) <= 1000);//空转1s
System.out.println("空转1S结束");
// 阻塞
LockSupport.park();
System.out.println(Thread.currentThread().getName() + " 是否被中断:" + Thread.currentThread().isInterrupted());
},"TH");
thread.start();
thread.interrupt();
// sout 空转1S结束
// TH 是否被中断:true
LockSupport.park/unpark VS Object.wait\notify
- park 不需要获取对象的锁,wait必须先获取对象的锁
- unpark 可以先于 park执行
- unpark 可以指定唤醒的线程,而notify不可控
- LockSupport 可指定Thread
- Object.wait\notify 只能在获取锁的前提下执行-即synchronized修饰的代码块
使用例子-源码中提供
先进先出非重入锁的实现
* class FIFOMutex {
* private final AtomicBoolean locked = new AtomicBoolean(false);
* private final Queue<Thread> waiters
* = new ConcurrentLinkedQueue<Thread>();
*
* public void lock() {
* boolean wasInterrupted = false;
* Thread current = Thread.currentThread();
* waiters.add(current);
*
* // Block while not first in queue or cannot acquire lock
* while (waiters.peek() != current ||
* !locked.compareAndSet(false, true)) {
* LockSupport.park(this);
* if (Thread.interrupted()) // ignore interrupts while waiting
* wasInterrupted = true;
* }
*
* waiters.remove();
* if (wasInterrupted) // reassert interrupt status on exit
* current.interrupt();
* }
*
* public void unlock() {
* locked.set(false);
* LockSupport.unpark(waiters.peek());
* }
* }}