admin管理员组文章数量:1581619
概述
LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。LockSupport 提供park()和unpark()方法实现阻塞线程和解除线程阻塞,LockSupport和每个使用它的线程都与一个许可(permit)关联。permit相当于1,0的开关,默认是0,调用一次unpark就加1变成1,调用一次park会消费permit, 也就是将1变成0,同时park立即返回。再次调用park会变成block(因为permit为0了,会阻塞在这里,直到permit变为1), 这时调用unpark会把permit置为1。每个线程都有一个相关的permit, permit最多只有一个,重复调用unpark也不会积累。
park()和unpark()不会有 “Thread.suspend和Thread.resume所可能引发的死锁” 问题,由于许可的存在,调用 park 的线程和另一个试图将其 unpark 的线程之间的竞争将保持活性。
如果调用线程被中断,则park方法会返回。同时park也拥有可以设置超时时间的版本。
需要特别注意的一点:park 方法还可以在其他任何时间“毫无理由”地返回,因此通常必须在重新检查返回条件的循环里调用此方法。从这个意义上说,park 是“忙碌等待”的一种优化,它不会浪费这么多的时间进行自旋,但是必须将它与 unpark 配对使用才更高效。
三种形式的 park 还各自支持一个 blocker 对象参数。此对象在线程受阻塞时被记录,以允许监视工具和诊断工具确定线程受阻塞的原因。(这样的工具可以使用方法 getBlocker(java.lang.Thread) 访问 blocker。)建议最好使用这些形式,而不是不带此参数的原始形式。在锁实现中提供的作为 blocker 的普通参数是 this。
park方法仅适用于以下形式的结构(在循环体内调用):
while (!canProceed()) { ... LockSupport.park(this); }}
看一个Java docs中的示例用法:一个先进先出非重入锁类的框架
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 ||
!lockedpareAndSet(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.park()源码分析
park()方法在permit可用时,会消费permit并立即返回;否则会被阻塞直到以下情形之一发生:
1、其它线程调用unpark()方法唤醒当前线程;
2、其它线程调用Thread.interrupt()方法中断当前线程;
3、虚假唤醒发生。
park方法通过调用unsafe类的park方法实现,源码如下:
public static void park() {
UNSAFE.park(false, 0L);
}
unsafe类的park()方法实现如下,通过调用thread类的成员变量parker,调用parker的park方法实现:
unsafe.cpp
UNSAFE_ENTRY(void, Unsafe_Park(JNIEnv *env, jobject unsafe, jboolean isAbsolute, jlong time))
UnsafeWrapper("Unsafe_Park");
EventThreadPark event;
#ifndef USDT2
HS_DTRACE_PROBE3(hotspot, thread__park__begin, thread->parker(), (int) isAbsolute, time);
#else /* USDT2 */
HOTSPOT_THREAD_PARK_BEGIN(
(uintptr_t) thread->parker(), (int) isAbsolute, time);
#endif /* USDT2 */
JavaThreadParkedState jtps(thread, time != 0);
thread->parker()->park(isAbsolute != 0, time); //调用parker的park方法
#ifndef USDT2
HS_DTRACE_PROBE1(hotspot, thread__park__end, thread->parker());
#else /* USDT2 */
HOTSPOT_THREAD_PARK_END(
(uintptr_t) thread->parker());
#endif /* USDT2 */
oop obj = thread->current_park_blocker();
if (event.should_commit()) {
event.set_klass(obj ? obj->klass() : (klassOop)NULL);
event.set_timeout(time);
event.set_address(obj ? (TYPE_ADDRESS) (uintptr_t) obj : 0);
eventmit();
}
UNSAFE_END
在HotSpot中,每个java线程都有一个Parker的实例
thread.hpp
// JSR166 per-thread parker
private:
Parker* _parker;
public:
Parker* parker() { return _parker; }
parker的定义如下:
从Parker定义不难看出:
-
定义私有属性_counter:可以理解为是否可以调用park的一个许可证,只有_count > 0的时候才能调用;
-
提供public方法park和unpark支撑阻塞/唤醒线程;
-
Parker继承PlatformParker;
park.hpp
class Parker : public os::PlatformParker {
private:
volatile int _counter ;
Parker * FreeNext ;
JavaThread * AssociatedWith ; // Current association
public:
Parker() : PlatformParker() {
_counter = 0 ;
FreeNext = NULL ;
AssociatedWith = NULL ;
}
protected:
~Parker() { ShouldNotReachHere(); }
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();
// Lifecycle operators
static Parker * Allocate (JavaThread * t) ;
static void Release (Parker * e) ;
private:
static Parker * volatile FreeList ;
static volatile int ListLock ;
};
linux平台下Parker的park方法实现
1、判断是否需要阻塞等待,如果_counter > 0,不需要等待,将_counter置为0,返回;
2、步骤1不成功,构造当前线程的ThreadBlockInVM,检查_counter > 0是否成立,成立则将_counter设置为0,unlock mutex,返回;
3、步骤2不成功,根据等待时间调用不同的等待函数等待,如果等待返回正确,将_counter置为0,unlock mutex,返回,park调用成功。
os_linux.cpp
void Parker::park(bool isAbsolute, jlong time) {
// Ideally we'd do something useful while spinning, such
// as calling unpackTime().
// Optional fast-path check:
// Return immediately if a permit is available.
// We depend on Atomic::xchg() having full barrier semantics
// since we are doing a lock-free update to _counter.
if (Atomic::xchg(0, &_counter) > 0) return; //counter>0,使用xchg指令置为0,然后直接返回
Thread* thread = Thread::current();
assert(thread->is_Java_thread(), "Must be JavaThread");
JavaThread *jt = (JavaThread *)thread;
// Optional optimization -- avoid state transitions if there's an interrupt pending.
// Check interrupt before trying to wait
if (Thread::is_interrupted(thread, false)) {
return;
}
// Next, demultiplex/decode time arguments
timespec absTime;
if (time < 0 || (isAbsolute && time == 0) ) { // don't wait at all
return;
}
if (time > 0) {
unpackTime(&absTime, isAbsolute, time);
}
// Enter safepoint region
// Beware of deadlocks such as 6317397.
// The per-thread Parker:: mutex is a classic leaf-lock.
// In particular a thread must never block on the Threads_lock while
// holding the Parker:: mutex. If safepoints are pending both the
// the ThreadBlockInVM() CTOR and DTOR may grab Threads_lock.
ThreadBlockInVM tbivm(jt); //构造当前线程的ThreadBlockInVM
// Don't wait if cannot get lock since interference arises from
// unblocking. Also. check interrupt before trying wait
if (Thread::is_interrupted(thread, false) || pthread_mutex_trylock(_mutex) != 0) {
return;
}
int status ;
if (_counter > 0) { // no wait needed counter>0,不需要等待
_counter = 0; //将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;
}
#ifdef ASSERT
// Don't catch signals while blocked; let the running threads have the signals.
// (This allows a debugger to break into the running thread.)
sigset_t oldsigs;
sigset_t* allowdebug_blocked = os::Linux::allowdebug_blocked_signals();
pthread_sigmask(SIG_BLOCK, allowdebug_blocked, &oldsigs);
#endif
OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);
jt->set_suspend_equivalent();
// cleared by handle_special_suspend_equivalent_condition() or java_suspend_self()
assert(_cur_index == -1, "invariant");
if (time == 0) {
_cur_index = REL_INDEX; // arbitrary choice when not timed
status = pthread_cond_wait (&_cond[_cur_index], _mutex) ; //调用pthread_cond_wait
} else {
_cur_index = isAbsolute ? ABS_INDEX : REL_INDEX;
//调用pthread_cond_timedwait
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());
}
}
_cur_index = -1;
assert_status(status == 0 || status == EINTR ||
status == ETIME || status == ETIMEDOUT,
status, "cond_timedwait");
#ifdef ASSERT
pthread_sigmask(SIG_SETMASK, &oldsigs, NULL);
#endif
_counter = 0 ; //从等待函数返回后,将counter置为0
status = pthread_mutex_unlock(_mutex) ;
assert_status(status == 0, status, "invariant") ;
// Paranoia to ensure our locked and lock-free paths interact
// correctly with each other and Java-level accesses.
OrderAccess::fence();
// If externally suspended while waiting, re-suspend
if (jt->handle_special_suspend_equivalent_condition()) {
jt->java_suspend_self();
}
}
从上面代码可以看出,LockSupprt.park()最终是调用pthread_cond_wait实现的。
LockSupport.unpark()源码分析
unpark(Thread thred)方法为指定线程生成可用的permit。如果指定线程被阻塞在park方法上,那么调用unpark方法将会解除对它的阻塞。如果指定线程没有被阻塞,那么它将保证下一次指定线程调用park方法不会被阻塞。
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
parker的unpark方法实现如下:
- 将_counter置为1;
- 判断之前_counter的值:
- 小于1时,调用pthread_cond_signal唤醒在park中等待的线程;
- 等于1时,unlock mutex,返回。
os_linux.cpp
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") ;
}
}
从上面代码可以看出,LockSupprt.unpark()最终是调用pthread_cond_wait唤醒阻塞在条件变量的线程。
Parker和ParkEvent的区别
在os_linux.cpp中,存在两种形式的park/unpark功能:
1、PlatformEvent实现的park,unpark方法:
PlatformEvent::park();
PlatformEvent::park(jlong millis);
PlatformEvent::unpark()
2、Parker实现的park,unpark方法:
Parker::park(bool isAbsolute, jlong time);
Parker::unpark();
它们的区别可以在park.hpp的源码注释中看到:
park.hpp
// The base-class, PlatformEvent, is platform-specific while the ParkEvent is
// platform-independent. PlatformEvent provides park(), unpark(), etc., and
// is abstract -- that is, a PlatformEvent should never be instantiated except
// as part of a ParkEvent.
// Equivalently we could have defined a platform-independent base-class that
// exported Allocate(), Release(), etc. The platform-specific class would extend
// that base-class, adding park(), unpark(), etc.
//
// A word of caution: The JVM uses 2 very similar constructs:
// 1. ParkEvent are used for Java-level "monitor" synchronization.
// 2. Parkers are used by JSR166-JUC park-unpark.
//
// We'll want to eventually merge these redundant facilities and use ParkEvent.
意思是说ParkEvent是用于java级别的synchronize关键字,Parker是JSR166来的并发工具集合,后面会统一使用ParkEvent。
ParkerEvent继承了PlatformEvent。基类PlatformEvent是特定于平台的,而ParkEvent则是平台无关的。
Parker继承自PlatformParker。
ParkerEvent中的park,unpark方法用于实现Java的object.wait()方法和object.notify()方法;
Parker中的park,unpark方法用于实现Java的Locksupprt.park()方法和Locksupprt.unpark()方法;
参考:LockSupport API
浅谈Java并发编程系列(八)—— LockSupport原理剖析
简书 java并发编程之LockSupport
LockSupport源码阅读与分析
本文标签: 源码JVMLockSupport
版权声明:本文标题:jvm源码分析之LockSupport 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/xitong/1725889333a1047370.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论