前言
在上一篇文章线程池返回值Future中,源码分析线程池结果获取阻塞的原因。
LockSupport.unpark(t);
LockSupport.parkNanos(this, nanos);或者LockSupport.park(this);
使用unpark唤醒线程,使用park阻塞线程,下面介绍他们的作用以及与wait和notify或者notifyAll的区别。
1. LockSupport.unpark(t);和LockSupport.park(this);源码解析
1.1 代理类
public class LockSupport {
private LockSupport() {} // Cannot be instantiated.
private static void setBlocker(Thread t, Object arg) {
// Even though volatile, hotspot doesn't need a write barrier here.
UNSAFE.putObject(t, parkBlockerOffset, arg);
}
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
/**
* Disables the current thread for thread scheduling purposes unless the
* permit is available.
*
* If the permit is available then it is consumed and the call returns
* immediately; otherwise
* the current thread becomes disabled for thread scheduling
* purposes and lies dormant until one of three things happens:
*
*
* - Some other thread invokes {@link #unpark unpark} with the
* current thread as the target; or
*
*
- Some other thread {@linkplain Thread#interrupt interrupts}
* the current thread; or
*
*
- The call spuriously (that is, for no reason) returns.
*
*
* This method does not report which of these caused the
* method to return. Callers should re-check the conditions which caused
* the thread to park in the first place. Callers may also determine,
* for example, the interrupt status of the thread upon return.
*
* @param blocker the synchronization object responsible for this
* thread parking
* @since 1.6
*/
public static void park(Object blocker) {
//当前线程
Thread t = Thread.currentThread();
//设置当前线程的blocker,为参数blocker
setBlocker(t, blocker);
//阻塞线程
UNSAFE.park(false, 0L);
//线程blocker释放
setBlocker(t, null);
}
我们看看 setBlocker(t, blocker);
private static void setBlocker(Thread t, Object arg) {
// Even though volatile, hotspot doesn't need a write barrier here.
UNSAFE.putObject(t, parkBlockerOffset, arg);
}
使用UNSAFE对Thread的parkBlockerOffset偏移量的属性设置值。整个操作是原子性的。
//两个很实用的方法
//对object对象的offset偏移量的属性,设置update的值
public native void putObject(Object object, long valueOffset, Object update);
//对object的offset偏移量的属性,此属性的值比对expect主存的值,如果想等就置为update并返回true;否则返回false
public final native boolean compareAndSwapObject(Object object, long valueOffset, Object expect, Object update);
获取偏移量的方法,class字节码获取,比如这里的parkBlockerOffset
// 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); }
}
源码分析
LockSupport其实是一个静态代理类,功能实现是用Unsafe类里面的native方法。起源于JDK1.6,JDK1.5只能用wait和notify.
//isAbsolute是时间的修饰,绝对或者相对,false就是相对时间
public native void park(boolean isAbsolute, long time);
public native void unpark(Object thread);
根据API的说明
unpark函数为线程提供“许可(permit)”,线程调用park函数则等待“许可”,但“许可”是一次性的。
比如:线程A连续调用了N(N>1)次unpark函数,当线程B调用park函数就使用掉这个“许可”,如果线程A再次调用一次park,则进入等待状态。如果线程A处于等待许可状态,再次调用park,则会永远等待下去,调用unpark也无法唤醒。
1.2 park和unpark对比wait/notify/notifyAll
unpark函数能够先于park调用,无时序限制。这个非常有用,事先准备,需要线程等待时,如果条件具备可以立即执行。
在JDK5中,是用wait/notify/notifyAll来同步的,必须要wait调用成功才能,才能notify,而且使用notify只能唤醒一个线程,一般使用notifyAll方法。
/** Only one thread at a time can own an object's monitor.
*
* @throws IllegalMonitorStateException if the current thread is not
* the owner of this object's monitor.
* @see java.lang.Object#notifyAll()
* @see java.lang.Object#wait()
*/
public final native void notify();
notify:Only one thread at a time can own an object's monitor.
2. OpenJDK源码分析
由于是UNSAFE 的 native方法,要深入本质分析,必须看源码,下面来看C++源码
Parker的unpark方法
void Parker::unpark() {
int s, status ;
//加锁
status = os::Solaris::mutex_lock (_mutex) ;
//断言加锁成功与否
assert (status == 0, "invariant") ;
//缓存_counter
s = _counter;
//_counter置为1,唤醒状态
_counter = 1;
//释放锁
status = os::Solaris::mutex_unlock (_mutex) ;
//断言锁释放
assert (status == 0, "invariant") ;
if (s < 1) {
//_counter只有0,1;_counter为0,等待状态才能执行cond_signal唤醒操作;等于1即唤醒状态,无需操作
status = os::Solaris::cond_signal (_cond) ;
//断言成功
assert (status == 0, "invariant") ;
}
}
可以看出 _counter变量决定线程的等待和唤醒,唤醒状态_counter为1,等待状态_counter为0
下面看Parker的park方法
void Parker::park(bool isAbsolute, jlong time) {
//信号量_counter标记是否已经是唤醒状态
if (_counter > 0) {
//等待状态
_counter = 0 ;
//阻塞线程
OrderAccess::fence();
return ;
}
//如果已经等待
//当前线程指针
Thread* thread = Thread::current();
assert(thread->is_Java_thread(), "Must be JavaThread");
JavaThread *jt = (JavaThread *)thread;
//如果已经是interrupt,没有状态更新的必要
if (Thread::is_interrupted(thread, false)) {
return;
}
timespec absTime;
//park等待操作超时 don't wait at all
if (time < 0) { // don't wait at all
return;
}
if (time > 0) {
//转换时间
unpackTime(&absTime, isAbsolute, time);
}
ThreadBlockInVM tbivm(jt);
//中断,加锁失败
if (Thread::is_interrupted(thread, false) ||
os::Solaris::mutex_trylock(_mutex) != 0) {
return;
}
int status ;
//再次重试
if (_counter > 0) { // no wait needed
//重复第一步操作
_counter = 0;
//释放刚刚加的锁
status = os::Solaris::mutex_unlock(_mutex);
assert (status == 0, "invariant") ;
OrderAccess::fence();
return;
}
//下面是一些满足条件的编译
//操作cond_wait,cond_timedwait 执行,使线程等待
#ifdef ASSERT
sigset_t oldsigs;
sigset_t* allowdebug_blocked = os::Solaris::allowdebug_blocked_signals();
thr_sigsetmask(SIG_BLOCK, allowdebug_blocked, &oldsigs);
#endif
OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);
jt->set_suspend_equivalent();
#if defined(__sparc) && defined(COMPILER2)
if (ClearFPUAtPark) { _mark_fpu_nosave() ; }
#endif
if (time == 0) {
status = os::Solaris::cond_wait (_cond, _mutex) ;
} else {
status = os::Solaris::cond_timedwait (_cond, _mutex, &absTime);
}
assert_status(status == 0 || status == EINTR ||
status == ETIME || status == ETIMEDOUT,
status, "cond_timedwait");
#ifdef ASSERT
thr_sigsetmask(SIG_SETMASK, &oldsigs, NULL);
#endif
_counter = 0 ;
status = os::Solaris::mutex_unlock(_mutex);
assert_status(status == 0, status, "mutex_unlock") ;
if (jt->handle_special_suspend_equivalent_condition()) {
jt->java_suspend_self();
}
OrderAccess::fence();
}
3. 总结
一个Parker实例对应一个Java的线程,源码分析Parker使用mutex(mutex_trylock,mutex_lock,mutex_unlock),condition(cond_signal ,cond_timedwait,cond_wait )来实现的。
Parker类里的_counter全局属性,记录许可(permit)。