LockSupport
约 619 字大约 2 分钟
LockSupport
在 JSR166
之前,没有 Java API
可用于阻塞和解除阻塞线程以创建不基于内置监视器的同步器。唯一的候选对象是 Thread.suspend
和 Thread.resume
,它们无法使用,因为它们遇到了无法解决的竞争问题:如果解除阻塞的线程在阻塞线程执行 suspend
之前调用了 resume
,则恢复操作将无效。
java.util.concurrent.locks
包包含一个 LockSupport
类,其中包含解决此问题的方法。方法 LockSupport.park
阻塞当前线程,除非或直到发出 LockSupport.unpark
。 (也允许虚假唤醒。)取消驻留的呼叫不“计数”,因此在驻留之前多次取消驻留只会解除对单个驻留的阻塞。此外,这适用于每个线程,而不是每个同步器。由于先前使用的“剩余” unpark,在新同步器上调用 park 的线程可能会立即返回。然而,在没有 unpark 的情况下,它的下一次调用将被阻塞。虽然可以明确清除此状态,但这样做是不值得的。在需要时多次调用 park 会更有效率。
概念
- park进行线程阻塞
- unpark解除线程阻塞
代码
public class LockSupport {
private LockSupport() {} // Cannot be instantiated.
// 其他代码请自行查看
// cas的一些属性
// 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 = 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); }
}
}
代码解释
从这个工具类来看,其底层依然调用的是unsafe类,核心调用有putObject、unpark、park、getInt、objectFieldOffset
,我们主要看前3个
park(Object blocker)
// 可以在jstack的时候看到阻塞在那个对象上面
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
private static void setBlocker(Thread t, Object arg) {
// Even though volatile, hotspot doesn't need a write barrier here.
UNSAFE.putObject(t, parkBlockerOffset, arg);
}
// 在Thread类里面可以找到volatile Object parkBlocker;这样的一个属性值
public native void putObject(Object o, long offset, Object x);
// 阻塞当前线程
public native void park(boolean isAbsolute, long time);
unpark(Thread thread)
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
// 进行线程非阻塞
public native void unpark(Object thread);
总结
JSR166
之后提供了新的API
支持线程线程阻塞和非阻塞,对应了aqs设计文档中的Blocking and unblocking threads
,从这里也学到了unsafe的第二个特性,支持线程阻塞非阻塞。