LockSupport是基本的线程阻塞原语, 用于构建lock和其他同步类.
这个类将每个线程与一个permit进行关联(类似于java.util.concurrent.Semaphore一样的感觉). 如果permit可用的话, 调用park方法会消费掉permit, 然后直接返回. 否则, 会阻塞线程. 调用unpark会使得不可用的permit可用.(和Semaphore不同的是, permit不会累加, 每个线程最多只有一个permit).
因为使用了permit, park和unpark方法提供了有效的block和unblock线程的方法. 与已经废弃的Thread.suspend和Thread.resume方法不同. 线程A调用park, 另一个线程B调用unpark来unblock A, 这两者发生竞争, 仍然会使得线程A保持活性.
另外, 如果调用者线程被interrupted, park会返回, 同时park也有timeout支持. park方法可能会随时无原因的返回, 所以一般调用需要在一个检测condition是否可用的循环中使用. 在这个意义上, park可以当作是一个busy wait, 而不需要太多时间自旋.
三种形式的park都支持传入blocker参数. 这个对象在线程被阻塞等待permit时被记录下来, 可用于监控和诊断工具来分析当前线程因为何种原因而阻塞(这些工具通过使用getBlocker(Thread)方法来获取). 推荐使用这种能够传入bolcker对象参数的方法类型. 在lock实现中, 通常传入的blocker参数是this
这些方法同样也被设计用于创建更高层次的同步工具.
park方法被设计只用于以下的形式:
while (!canProceed()) { ... LockSupport.park(this); }}
在上面的代码中, park方法之前的, 无论是canProceed或其他方法都不应该涉及locking或block. 因为每个线程只有一个permit, 任何中间形式的使用park都会干扰想要的效果.
下面是一个简单的FIFO非重入锁的实现.
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());
}
}
LockSupport的方法:
//使用一个线程thread的permit可用. 如果这个thread之前阻塞在park方法上, 那么会unblock. 或者thread没有阻塞在park方法上, 下一次park方法时, thread会直接返回, 而不会阻塞. 不能保证线程没有启动时有效.
public static void unpark(Thread thread)
/**禁止程被调度, 除非permit可用
如果permit可用, 那么就会被直接消费, 然后park调用返回. 否则当前线程会被停止调度, 保持休眠状态直到以下三个条件之一发生:
1. 另外一个线程对其调用了unpark
2. 另外一个线程调用了Thread.interrupt(thread)方法, interrupt当前线程
3. 毫无理由的返回
这个方法并不会报告什么原因造成了返回, 调用者应该立即检查造成thread调用park的Condition, 同时, 调用者也可能需要检查thread的interrupt状态.
**/
public static void park(Object blocker)
//说明与上相同, nanos是线程阻塞的最长的纳秒数
public static void parkNanos(Object blocker, long nanos)
//说明同上. deadline是线程要阻塞到的绝对时间, Epoch时间戳, 单位为毫秒
public static void parkUntil(Object blocker, long deadline)
public static void park()
public static void parkNanos(long nanos)
public static void parkUntil(long deadline)
//获取某个Thread最近调用park时传入的blocker. 每次可能返回不同的值
public static Object getBlocker(Thread t)
//设置一个线程的blocker对象
private static void setBlocker(Thread t, Object arg)
blocker的原理:
设置blocker时设置的是Thread的一个字段parkBlocker, 通过UNSAFE类来获取其在Thread类中的偏移, 然后通过UNSAFE类在这个偏移上放置blocker的指针就可以了.
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> tk = Thread.class;
//通过UNSAFE类来获取Thread类中的parkBlocker在内存中在对象中的偏移
parkBlockerOffset = UNSAFE.objectFieldOffset
(tk.getDeclaredField("parkBlocker"));
.....
} catch (Exception ex) { throw new Error(ex); }
}
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) {
//使用UNSAFE的方法在对象的相应偏移位置放置对象指针
UNSAFE.putObject(t, parkBlockerOffset, arg);
}