概述
LockSupport 类提供了基本的线程同步原语,是实现 AbstractQueuedSynchronizer 和 ReentrantLock 的基础。
源码解析
LockSupport 最重要的就是加锁的 park、解锁的 unpark 方法了:
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.putObject(t, parkBlockerOffset, arg);
}
public static void unpark(Thread thread) {
if (thread != null)
unsafe.unpark(thread);
}
park 的入参 blocker 是阻塞线程的对象,通过 setBlocker 方法设置到 Thread 对象的 parkBlocker 域,用于线程监控和分析。unpark 的入参是将要解锁的线程。parker 方法的注释里提到了三种情况会造成 park 返回:unpark、中断、spurious wakeup。
Some other thread invokes {@link #unpark unpark} with the current thread as the target.
Some other thread {@linkplain Thread#interrupt interrupts} the current thread.
The call spuriously (that is, for no reason) returns.
关于 spurious wakeup 可以看看参考文献里的资料。
park 和 unpark 方法实际上是调用了 Unsafe 类里的函数:
// in sun.misc.Unsafe.java
// thread 参数标记要唤醒的线程
public native void unpark(Object thread);
// time 为 0L 时,表示不设置超时时间
// isAbsolute 参数为 true,表示 time 为超时唤醒的绝对时间;否则为相对时间
public native void park(boolean isAbsolute, long time);
从上面的代码可知,park 时可以设置超时时间,对应的 LockSupport 中也有能够设置绝对超时时间的 parkUntil,和相对超时时间的 parkNanos:
public static void parkUntil(long deadline) {
UNSAFE.park(true, deadline);
}
public static void parkNanos(long nanos) {
if (nanos > 0)
UNSAFE.park(false, nanos);
}
Unsafe 的 park 和 unpark 又是怎么实现的呢?在底层,每一个 Thread 对象的都有一个 Parker 实例,
// in vm/runtime/thread.hpp
// JSR166 per-thread parker
private:
Parker* _parker;
public:
Parker* parker() { return _parker; }
Parker 类继承自 PlatformParker,PlatformParker 的实现就是系统相关了,linux 的实现是利用了 POSIX 的 mutex 和 condition。
class Parker : public os::PlatformParker {
public:
// For simplicity of interface with Java, all forms of park (indefinite,
// relative, and absolute) are multiplexed into one call.
void park(bool isAbsolute, jlong time);
void unpark();
};
// in linux/vm/os_linux.hpp
class PlatformParker : public CHeapObj<mtInternal> {
protected:
pthread_mutex_t _mutex [1] ;
pthread_cond_t _cond [2] ; // one for relative times and one for abs.
};
Parker::park 方法中,主要是调用了 pthread_cond_wait 方法和 safe_cond_timedwait,safe_cond_timedwait 调用了 pthread_cond_timedwait。
void Parker::park(bool isAbsolute, jlong time) {
// 这中间省略了很多代码
if (time == 0) {
_cur_index = REL_INDEX; // arbitrary choice when not timed
status = pthread_cond_wait (&_cond[_cur_index], _mutex) ;
} else {
_cur_index = isAbsolute ? ABS_INDEX : REL_INDEX;
status = os::Linux::safe_cond_timedwait (&_cond[_cur_index], _mutex, &absTime) ;
if (status != 0 && WorkAroundNPTLTimedWaitHang) {
pthread_cond_destroy (&_cond[_cur_index]) ;
pthread_cond_init (&_cond[_cur_index], isAbsolute ? NULL : os::Linux::condAttr());
}
}
// 这里也省略了很多代码
status = pthread_mutex_unlock(_mutex) ;
assert_status(status == 0, status, "invariant") ;
// 内存栅栏,内存屏障
OrderAccess::fence();
}
int os::Linux::safe_cond_timedwait(pthread_cond_t *_cond, pthread_mutex_t *_mutex, const struct timespec *_abstime)
{
if (is_NPTL()) {
return pthread_cond_timedwait(_cond, _mutex, _abstime);
} else {
// 6292965: LinuxThreads pthread_cond_timedwait() resets FPU control
// word back to default 64bit precision if condvar is signaled. Java
// wants 53bit precision. Save and restore current value.
int fpu = get_fpu_control_word();
int status = pthread_cond_timedwait(_cond, _mutex, _abstime);
set_fpu_control_word(fpu);
return status;
}
}
Parker::unpark 设置 _counter 为1,再 unlock mutex,如果 _counter 之前小于 1,则调用 pthread_cond_signal 唤醒等待的线程。
void Parker::unpark() {
int s, status ;
status = pthread_mutex_lock(_mutex);
assert (status == 0, "invariant") ;
s = _counter;
_counter = 1;
if (s < 1) {
// thread might be parked
if (_cur_index != -1) {
// thread is definitely parked
if (WorkAroundNPTLTimedWaitHang) {
status = pthread_cond_signal (&_cond[_cur_index]);
assert (status == 0, "invariant");
status = pthread_mutex_unlock(_mutex);
assert (status == 0, "invariant");
} else {
status = pthread_mutex_unlock(_mutex);
assert (status == 0, "invariant");
status = pthread_cond_signal (&_cond[_cur_index]);
assert (status == 0, "invariant");
}
} else {
pthread_mutex_unlock(_mutex);
assert (status == 0, "invariant") ;
}
} else {
pthread_mutex_unlock(_mutex);
assert (status == 0, "invariant") ;
}
}
灵活的特性
在 Parker::park 中有这么一段代码,如果 _counter 大于 0,则立即返回。在上面 unpark 的代码中,我们看到 unpark 将 _counter 设置为 1,也就是说:两个线程之间的 park 和 unpark 不存在时序关系,可以先 unpark 再 park,不会造成死锁。这相对于存在依赖关系的 wait/notify 机制是一个巨大的优点。
void Parker::park(bool isAbsolute, jlong time) {
// 这中间省略了很多代码
int status ;
if (_counter > 0) { // no wait needed
_counter = 0;
status = pthread_mutex_unlock(_mutex);
assert (status == 0, "invariant") ;
// Paranoia to ensure our locked and lock-free paths interact
// correctly with each other and Java-level accesses.
OrderAccess::fence();
return;
}
// 这后面省略了更多代码
应用
AQS: AbstractQueuedSynchronizer
AbstractQueuedSynchronizer 中获取锁的代码如下,这里使用 for 循环,而不是在 park 返回后就立即返回,也是为了排除中断、虚假唤醒等并非因资源可用而唤醒的情况。
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
FutureTask
在 FutureTask 中,等待操作完成的 awaitDone 大致分为以下步骤:
1. 先检查是否存在中断,是则抛异常 InterruptedException;
2. 是否已经完成,是则返回;
3. 进入等待队列中;
4. 当设置了超时时间 nanos 时,调用 LockSupport.parkNanos 方法等待;
5. 没有设置超时时间时,调用 LockSupport.park 方法等待。
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
for (;;) {
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
int s = state;
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
else if (s == COMPLETING) // cannot time out yet
Thread.yield();
else if (q == null)
q = new WaitNode();
else if (!queued)
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
LockSupport.parkNanos(this, nanos);
}
else
LockSupport.park(this);
}
}
参考资料
Java 的 LockSupport.park() 实现分析
多线程编程中条件变量和虚假唤醒(spurious wakeup)的讨论
Why does pthread_cond_wait have spurious wakeups?