深入JVM源码篇-2-探索同步器
目录
1. 目标
我探索同步器的JVM源码,采取的步骤为:先找到同步器类,然后从数据模型入手,了解涉及到的关键模型,接着重点看关键行为的逻辑。
希望达到的目标:了解同步器的JVM实现,何为偏向、轻量级、重量级。
2. 关键类
markWord
ObjectWaiter
ObjectMonitor
ObjectSynchronizer
3. 数据模型说明
3.1. markWord
描述了一个object的header,与偏向锁有关。依据markword来判断是否偏向锁和轻量级锁。
// markWord 描述了一个object的header。header分为32位或64位。依据markword来判断是否偏向锁和轻量级锁。
// 偏向锁模式 表示锁偏向一个给定线程或匿名偏向。
// 若偏向一个给定线程,则该线程可不通过原子操作即可加锁或释放锁。
// 低3位用于配置偏向模式。
// [JavaThread* | epoch | age | 1 | 01] lock is biased toward given thread
// [0 | epoch | age | 1 | 01] lock is anonymously biased
// 低2位可描述3中情况:已加锁、已释放锁、监视器
// [ptr | 00] locked ptr points to real header on stack
// [header | 0 | 01] unlocked regular object header
// [ptr | 10] monitor inflated lock (header is wapped out)
// [ptr | 11] marked used to mark an object
// [0 ............ 0| 00] inflating inflation in progress
class markWord {
private:
uintptr_t _value;
.....
}
3.2. ObjectWaiter
代表 争抢监视器的线程,一个线程对应一个ObjectWaiter,定义一个双向链表。
// ObjectWaiter 代表 争抢监视器的线程,一个线程对应一个ObjectWaiter
class ObjectWaiter : public StackObj {
public:
enum TStates { TS_UNDEF, TS_READY, TS_RUN, TS_WAIT, TS_ENTER, TS_CXQ };
// ObjectWaiter 双向链表
ObjectWaiter* volatile _next;
ObjectWaiter* volatile _prev;
// 关联的线程
Thread* _thread;
jlong _notifier_tid;
ParkEvent * _event;
// 是否已被notify
volatile int _notified;
// 状态
volatile TStates TState;
// 是否可以去争抢监视器
bool _active; // Contention monitoring is enabled
public:
ObjectWaiter(Thread* thread);
// 决定notify ObjectWaiter后,并通过该接口,让其重新争抢监视器
void wait_reenter_begin(ObjectMonitor *mon);
void wait_reenter_end(ObjectMonitor *mon);
};
3.3. ObjectMonitor
代表对象的监视器,实现重量级\轻量级锁版本。
关键field有markWord类型的_header、 占用者owner、ObjectWaiter的链表_EntryList、ObjectWaiter的链表waitSet。
// ObjectMonitor 实现了JavaMonitor的重量级版本。由于Object.wait()的使用或争抢,扩充了轻量级锁版本。
class ObjectMonitor : public CHeapObj<mtInternal> {
...
// The sync code expects the header field to be at offset zero (0).
// Enforced by the assert() in header_addr().
volatile markWord _header; // displaced object header word - mark
// 监视器 关联的 对象
WeakHandle _object; // backward object pointer
...
// 当前占用监视器的线程
void* _owner; // pointer to owning thread OR BasicLock
// 之前占用监视器的线程ID
volatile jlong _previous_owner_tid; // thread id of the previous owner of the monitor
// ObjectMonitor 单向链
ObjectMonitor* _next_om; // Next ObjectMonitor* linkage
// 重入数,0表示第一次获取监视器
volatile intx _recursions; // recursion count, 0 for first entry
// 由获取监视器而阻塞的线程构成的链表
ObjectWaiter* volatile _EntryList; // Threads blocked on entry or reentry.
// The list is actually composed of WaitNodes,
// acting as proxies for Threads.
// 由于获取监视器而阻塞的近期线程构成的链表
ObjectWaiter* volatile _cxq; // LL of recently-arrived threads blocked on entry.
Thread* volatile _succ; // Heir presumptive thread - used for futile wakeup throttling
Thread* volatile _Responsible;
// 正在争抢监视器的线程个数
jint _contentions; // Number of active contentions in enter(). It is used by is_busy()
// along with other fields to determine if an ObjectMonitor can be
// deflated. It is also used by the async deflation protocol. See
// ObjectMonitor::deflate_monitor().
protected:
// 调用监视器wait()且没有被notify的线程构成的链表
ObjectWaiter* volatile _WaitSet; // LL of threads wait()ing on the monitor
// 调用监视器wait()且没有被notify的线程数量
volatile jint _waiters; // number of waiting threads
private:
// 用于保护waitset,操作waitset前后需要使用自旋锁来加锁和释放锁
volatile int _WaitSetLock; // protects Wait Queue - simple spinlock
....
}
3.4. ObjectSynchronizer
对象同步器
// 对象同步器,定义行为enter、exit、wait、notify、notifyall、reenter、inflate(从轻量级膨胀为重量级)
class ObjectSynchronizer{
...
}
4. 关键行为
4.1. ObjectSynchronizer#enter
概述为:判断是否采取轻量级,否则重量级。
可以看到,通过markword判断是否使用轻量级版本,且轻量级版本只是尝试一次cas设置监视器的header域。
void ObjectSynchronizer::enter(Handle obj, BasicLock* lock, TRAPS) {
...
markWord mark = obj->mark();
// 不允许使用偏向锁
assert(!mark.has_bias_pattern(), "should not see bias pattern here");
// 轻量级版本 BasicLock
// last bits = 001 参见markword的注释: [header | 0 | 01] unlocked regular object header
if (mark.is_neutral()) {
// unlocked情况,直接cas改变lock的mark,成功则表示获取监视器成功
// Anticipate successful CAS -- the ST of the displaced mark must
// be visible <= the ST performed by the CAS.
lock->set_displaced_header(mark);
if (mark == obj()->cas_set_mark(markWord::from_pointer(lock), mark)) {
return;
}
// 失败则使用重量级版本 Fall through to inflate() ...
} else if (mark.has_locker() &&
THREAD->is_lock_owned((address)mark.locker())) {
// mark 为 locked,参见markword:[ptr | 00] locked ptr points to real header on stack
// 且owner是当前线程
...
lock->set_displaced_header(markWord::from_pointer(NULL));
return;
}
// The object header will never be displaced to this lock,
// so it does not matter what the value is, except that it
// must be non-zero to avoid looking like a re-entrant lock,
// and must not look locked either.
lock->set_displaced_header(markWord::unused_mark());
// An async deflation can race after the inflate() call and before
// enter() can make the ObjectMonitor busy. enter() returns false if
// we have lost the race to async deflation and we simply try again.
// 重量级版本
while (true) {
ObjectMonitor* monitor = inflate(THREAD, obj(), inflate_cause_monitor_enter);
if (monitor->enter(THREAD)) {
return;
}
}
}
4.2. ObjectMonitor#enter
按照ObjectSynchronizer::enter的注释,轻量级逻辑失败将膨胀为重量级锁,所以ObjectMonitor::enter就是重量级锁。
大致步骤:首先若占用者的owner为null则直接占用成功,其次若重入则占用成功,最后争抢。
轻量级版本是判断和设置markword,与此对比,重量级版本是判断和设置owner。
从代码可看出,所谓重量级版本,就是自旋方式获取监视器。JDK的AQS也是自旋方式,因此我的看法是AQS就是重量级。
// Enter support
// 重量级版本
bool ObjectMonitor::enter(TRAPS) {
// The following code is ordered to check the most common cases first
// and to reduce RTS->RTO cache line upgrades on SPARC and IA32 processors.
Thread * const Self = THREAD;
void* cur = try_set_owner_from(NULL, Self);
// 当前监视器没有被占用的情况,直接占用成功
if (cur == NULL) {
assert(_recursions == 0, "invariant");
return true;
}
// 之前的占用者就是当前线程,则重入成功
if (cur == Self) {
// TODO-FIXME: check for integer overflow! BUGID 6557169.
_recursions++;
return true;
}
// 之前的占用者就是当前线程,则重入成功
if (Self->is_lock_owned((address)cur)) {
assert(_recursions == 0, "internal state error");
_recursions = 1;
set_owner_from_BasicLock(cur, Self); // Convert from BasicLock* to Thread*.
return true;
}
// We've encountered genuine contention.
assert(Self->_Stalled == 0, "invariant");
Self->_Stalled = intptr_t(this);
// 尝试自旋加锁,自旋有限次数
if (TrySpin(Self) > 0) {
assert(...);
Self->_Stalled = 0;
return true;
}
assert(...);
JavaThread * jt = Self->as_Java_thread();
assert(jt->thread_state() != _thread_blocked, "invariant");
...
{ // Change java thread status to indicate blocked on monitor enter.
...
for (;;) {
jt->set_suspend_equivalent();
// cleared by handle_special_suspend_equivalent_condition()
// or java_suspend_self()
EnterI(THREAD);
// 已经获取监视器锁,但是要求该线程没有被要求挂起
if (!ExitSuspendEquivalent(jt)) break;
// 到这一步,表示已经获得监视器锁,但是需要等待'挂起当前线程'的其他线程A。为了符合逻辑,所以线程不可以在处于挂起时进入监视器。
_recursions = 0;
_succ = NULL;
exit(false, Self);
// 因为被请求挂起,所以执行挂起操作。
jt->java_suspend_self();
}
Self->set_current_pending_monitor(NULL);
...
}
add_to_contentions(-1);
assert(contentions() >= 0, "must not be negative: contentions=%d", contentions());
Self->_Stalled = 0;
...
return true;
}
4.3. ObjectMonitor::EnterI
获取监视器锁,主要通过自旋方式。
void ObjectMonitor::EnterI(TRAPS) {
Thread * const Self = THREAD;
assert(Self->as_Java_thread()->thread_state() == _thread_blocked, "invariant");
// 尝试一次,使用cas尝试这是owner
if (TryLock (Self) > 0) {
assert(...);
return;
}
...
// 尝试有限次数的自旋
if (TrySpin(Self) > 0) {
assert(...);
return;
}
...
// 将当前线程放到‘_cxq近期争抢者链表’的头部
ObjectWaiter * nxt;
for (;;) {
node._next = nxt = _cxq;
// 插入成功则退出循环
if (Atomic::cmpxchg(&_cxq, nxt, &node) == nxt) break;
// 尝试一次,使用cas尝试这是owner
if (TryLock (Self) > 0) {
assert(...);
return;
}
}
...
int nWakeups = 0;
int recheckInterval = 1;
// 自旋 park 尝试获取监视器
for (;;) {
// 尝试一次,使用cas尝试这是owner
if (TryLock(Self) > 0) break;
assert(owner_raw() != Self, "invariant");
// park self
if (_Responsible == Self) {
Self->_ParkEvent->park((jlong) recheckInterval);
// Increase the recheckInterval, but clamp the value.
recheckInterval *= 8;
if (recheckInterval > MAX_RECHECK_INTERVAL) {
recheckInterval = MAX_RECHECK_INTERVAL;
}
} else {
Self->_ParkEvent->park();
}
// 尝试一次,使用cas尝试这是owner
if (TryLock(Self) > 0) break;
...
}
// 到此,表示已经获取监视器锁
...
return;
}
5. 总结
从实现原理角度,对比ObjectSynchronizer与AQS,除了ObjectSynchronizer的轻量级版本,两者都使用到链表、自旋、park、cas,个人认为没有特别的差异。