重量级锁
例1: 这里创建了两个线程t1,t2,并让他们执行去竞争对象a的锁,并打印对象a的对象头,然后等待t1,t2执行完毕后,再次打印对象a的头部信息。
public class Test {
static class A {}
static A a = new A();
static class MyThread extends Thread{
@Override
public void run() {
synchronized (a){
try {
Thread.sleep(1000);
System.out.println(ClassLayout.parseInstance(a).toPrintable());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new MyThread();
Thread t2 = new MyThread();
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
test.Test$A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) ba 83 a5 02 (10111010 10000011 10100101 00000010) (44401594)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 9f c1 00 f8 (10011111 11000001 00000000 11111000) (-134168161)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
test.Test$A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) ba 83 a5 02 (10111010 10000011 10100101 00000010) (44401594)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 9f c1 00 f8 (10011111 11000001 00000000 11111000) (-134168161)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
test.Test$A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) ba 83 a5 02 (10111010 10000011 10100101 00000010) (44401594)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 9f c1 00 f8 (10011111 11000001 00000000 11111000) (-134168161)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
从输出结果我们可以看出,当t1和t2同时竞争对象a的锁时,a会膨胀为重量级锁。
然而这并不是说jvm直接给了对象a一把重量级锁。结合前文的知识,如果jvm是开启了偏向锁,那么对象a会从偏向锁,膨胀为轻量级锁,再膨胀为重量级锁的。期间可能会有偏向锁和轻量级锁的过度。如果没有开启,那么会从轻量级锁膨胀为重量级锁,期间也会出现轻量级锁的过度。所以我们需要根据具体的情况来设置jvm是否开启偏向锁,或者只是用重量级锁。
重量级锁加锁源码解析
如果该对象一个对象在进行轻量级锁的加锁或者解锁的过程中,cas替换mark word失败了,那么就会膨胀为重量级锁。我们先来看锁膨胀的代码。
//Mark Word可能有以下几种状态:
// * Inflated(膨胀完成) - 这种状态说明该锁已经是重量级锁了,不需要膨胀,直接返回
// * Stack-locked(轻量级锁) - 需要执行膨胀操作
// * INFLATING(膨胀中) - 正在膨胀中
// * Neutral(无锁) - 强制执行膨胀操作
// * BIASED(偏向锁) - 出现偏向锁是不合理的,需要抛出异常
ObjectMonitor * ATTR ObjectSynchronizer::inflate (Thread * Self, oop object) {
assert (Universe::verify_in_progress() ||
!SafepointSynchronize::is_at_safepoint(), "invariant") ;
EventJavaMonitorInflate event;
for (;;) {
const markOop mark = object->mark() ;
assert (!mark->has_bias_pattern(), "invariant") ;
//如果已经分配了监视器,那么直接返回
if (mark->has_monitor()) {
ObjectMonitor * inf = mark->monitor() ;
assert (inf->header()->is_neutral(), "invariant");
assert (inf->object() == object, "invariant") ;
assert (ObjectSynchronizer::verify_objmon_isinpool(inf), "monitor is invalid");
return inf ;
}
//如果其他线程正在膨胀这个锁,那么同步外层的for来进行自旋,等待锁膨胀完成
if (mark == markOopDesc::INFLATING()) {
TEVENT (Inflate: spin while INFLATING) ;
ReadStableMark(object) ;
continue ;
}
//如果已经分配了轻量锁,那么尝试分配重量级锁
if (mark->has_locker()) {
//omAlloc用来分配重量级锁,在每个线程中有一个omFreeList用来保存ObjectMonitor。
//分配ObjectMonitor时,会先从该线程的list获取一个未被使用的ObjectMonitor,如果获取不到
//就会从全局中gFreeList中获取。还是获取不到的情况下,才会去创建一个新的。
//新创建的ObjectMonitor会被连接到的gFreeList末尾
ObjectMonitor * m = omAlloc (Self) ;
m->Recycle();
m->_Responsible = NULL ;
m->OwnerIsThread = 0 ;
m->_recursions = 0 ;
//设置自旋获取重量级锁的次数 默认5000
m->_SpinDuration = ObjectMonitor::Knob_SpinLimit ; // Consider: maintain by type/class
//cas替换标志位正在膨胀
markOop cmp = (markOop) Atomic::cmpxchg_ptr (markOopDesc::INFLATING(), object->mark_addr(), mark) ;
if (cmp != mark) {
//释放锁的方法,与omAlloc相对应,释放锁时,ObjectMonitor会被连接在当前线程的omFreeList末尾
omRelease (Self, m, true) ;
//重试
continue ;
}
//栈中的displaced mark word
markOop dmw = mark->displaced_mark_helper() ;
assert (dmw->is_neutral(), "invariant") ;
m->set_header(dmw) ;
//设置ObjectMonitor的_owner为拥有对象轻量级锁的线程,而不是当前线程
//因为轻量级锁在退出同步代码块之后,会释放锁变成无锁状态。如果当前是轻量级锁,说明有已经获取到锁的线程正在执行同步代码块
//所以这里将已经获取到锁的线程那个线程设置到ObjectMonitor中
m->set_owner(mark->locker());
m->set_object(object);
guarantee (object->mark() == markOopDesc::INFLATING(), "invariant") ;
object->release_set_mark(markOopDesc::encode(m));
if (ObjectMonitor::_sync_Inflations != NULL) ObjectMonitor::_sync_Inflations->inc() ;
TEVENT(Inflate: overwrite stacklock) ;
//jvm参数,打印锁膨胀信息
if (TraceMonitorInflation) {
if (object->is_instance()) {
ResourceMark rm;
tty->print_cr("Inflating object " INTPTR_FORMAT " , mark " INTPTR_FORMAT " , type %s",
(void *) object, (intptr_t) object->mark(),
object->klass()->external_name());
}
}
if (event.should_commit()) {
post_monitor_inflate_event(&event, object);
}
return m ;
}
//剩下无锁的情况
assert (mark->is_neutral(), "invariant");
ObjectMonitor * m = omAlloc (Self) ;
m->Recycle();
m->set_header(mark);
//无锁就是当前线程自己加锁了,所以不要设置owner,只要修改OwnerIsThread标记为1就行
m->set_owner(NULL);
m->set_object(object);
m->OwnerIsThread = 1 ;
m->_recursions = 0 ;
m->_Responsible = NULL ;
//设置自旋获取重量级锁的次数 默认5000
m->_SpinDuration = ObjectMonitor::Knob_SpinLimit ; // consider: keep metastats by type/class
// 用CAS替换对象头的为重量级锁状态,这里将mointer的地址放入mark word中
if (Atomic::cmpxchg_ptr (markOopDesc::encode(m), object->mark_addr(), mark) != mark) {
//失败就释放锁,说明已经有别的线程在加锁了
m->set_object (NULL) ;
m->set_owner (NULL) ;
m->OwnerIsThread = 0 ;
m->Recycle() ;
omRelease (Self, m, true) ;
m = NULL ;
continue ;
}
if (ObjectMonitor::_sync_Inflations != NULL) ObjectMonitor::_sync_Inflations->inc() ;
TEVENT(Inflate: overwrite neutral) ;
//jvm参数,打印锁膨胀信息
if (TraceMonitorInflation) {
if (object->is_instance()) {
ResourceMark rm;
tty->print_cr("Inflating object " INTPTR_FORMAT " , mark " INTPTR_FORMAT " , type %s",
(void *) object, (intptr_t) object->mark(),
object->klass()->external_name());
}
}
if (event.should_commit()) {
post_monitor_inflate_event(&event, object);
}
return m ;
}
}
在膨胀之后,都会调用enter方法
ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);
void ATTR ObjectMonitor::enter(TRAPS) {
Thread * const Self = THREAD ;
void * cur ;
//这里的cur返回的是cas操作之前的值
cur = Atomic::cmpxchg_ptr (Self, &_owner, NULL) ;
//如果是无锁状态,那么经过cas就获得锁
if (cur == NULL) {
// Either ASSERT _recursions == 0 or explicitly set _recursions = 0.
assert (_recursions == 0 , "invariant") ;
assert (_owner == Self, "invariant") ;
// CONSIDER: set or assert OwnerIsThread == 1
return ;
}
//如果当前线程是自己,则重入次数+1
if (cur == Self) {
// TODO-FIXME: check for integer overflow! BUGID 6557169.
_recursions ++ ;
return ;
}
//如果之前已经是轻量级锁了,且该线程持有该对象的锁,现在需要膨胀的情况
if (Self->is_lock_owned ((address)cur)) {
assert (_recursions == 0, "internal state error");
//重入次数置为1
_recursions = 1 ;
//设置线程
_owner = Self ;
OwnerIsThread = 1 ;
return ;
}
// We've encountered genuine contention.
assert (Self->_Stalled == 0, "invariant") ;
Self->_Stalled = intptr_t(this) ;
//锁已经被其他线程持有,那么自旋获取
if (Knob_SpinEarly && TrySpin (Self) > 0) {
assert (_owner == Self , "invariant") ;
assert (_recursions == 0 , "invariant") ;
assert (((oop)(object()))->mark() == markOopDesc::encode(this), "invariant") ;
//如果成功直接返回
Self->_Stalled = 0 ;
return ;
}
assert (_owner != Self , "invariant") ;
assert (_succ != Self , "invariant") ;
assert (Self->is_Java_thread() , "invariant") ;
JavaThread * jt = (JavaThread *) Self ;
assert (!SafepointSynchronize::is_at_safepoint(), "invariant") ;
assert (jt->thread_state() != _thread_blocked , "invariant") ;
assert (this->object() != NULL , "invariant") ;
assert (_count >= 0, "invariant") ;
// Prevent deflation at STW-time. See deflate_idle_monitors() and is_busy().
// Ensure the object-monitor relationship remains stable while there's contention.
Atomic::inc_ptr(&_count);
JFR_ONLY(JfrConditionalFlushWithStacktrace<EventJavaMonitorEnter> flush(jt);)
EventJavaMonitorEnter event;
if (event.should_commit()) {
event.set_monitorClass(((oop)this->object())->klass());
event.set_address((uintptr_t)(this->object_addr()));
}
{ // Change java thread status to indicate blocked on monitor enter.
JavaThreadBlockedOnMonitorEnterState jtbmes(jt, this);
//到这里自旋获取锁失败了,将线程设置为pending状态
Self->set_current_pending_monitor(this);
DTRACE_MONITOR_PROBE(contended__enter, this, object(), jt);
if (JvmtiExport::should_post_monitor_contended_enter()) {
JvmtiExport::post_monitor_contended_enter(jt, this);
// The current thread does not yet own the monitor and does not
// yet appear on any queues that would get it made the successor.
// This means that the JVMTI_EVENT_MONITOR_CONTENDED_ENTER event
// handler cannot accidentally consume an unpark() meant for the
// ParkEvent associated with this ObjectMonitor.
}
OSThreadContendState osts(Self->osthread());
ThreadBlockInVM tbivm(jt);
// TODO-FIXME: change the following for(;;) loop to straight-line code.
for (;;) {
jt->set_suspend_equivalent();
// cleared by handle_special_suspend_equivalent_condition()
// or java_suspend_self()
EnterI (THREAD) ;
if (!ExitSuspendEquivalent(jt)) break ;
//
// We have acquired the contended monitor, but while we were
// waiting another thread suspended us. We don't want to enter
// the monitor while suspended because that would surprise the
// thread that suspended us.
//
_recursions = 0 ;
_succ = NULL ;
exit (false, Self) ;
jt->java_suspend_self();
}
Self->set_current_pending_monitor(NULL);
// We cleared the pending monitor info since we've just gotten past
// the enter-check-for-suspend dance and we now own the monitor free
// and clear, i.e., it is no longer pending. The ThreadBlockInVM
// destructor can go to a safepoint at the end of this block. If we
// do a thread dump during that safepoint, then this thread will show
// as having "-locked" the monitor, but the OS and java.lang.Thread
// states will still report that the thread is blocked trying to
// acquire it.
}
Atomic::dec_ptr(&_count);
assert (_count >= 0, "invariant") ;
Self->_Stalled = 0 ;
// Must either set _recursions = 0 or ASSERT _recursions == 0.
assert (_recursions == 0 , "invariant") ;
assert (_owner == Self , "invariant") ;
assert (_succ != Self , "invariant") ;
assert (((oop)(object()))->mark() == markOopDesc::encode(this), "invariant") ;
// The thread -- now the owner -- is back in vm mode.
// Report the glorious news via TI,DTrace and jvmstat.
// The probe effect is non-trivial. All the reportage occurs
// while we hold the monitor, increasing the length of the critical
// section. Amdahl's parallel speedup law comes vividly into play.
//
// Another option might be to aggregate the events (thread local or
// per-monitor aggregation) and defer reporting until a more opportune
// time -- such as next time some thread encounters contention but has
// yet to acquire the lock. While spinning that thread could
// spinning we could increment JVMStat counters, etc.
DTRACE_MONITOR_PROBE(contended__entered, this, object(), jt);
if (JvmtiExport::should_post_monitor_contended_entered()) {
JvmtiExport::post_monitor_contended_entered(jt, this);
// The current thread already owns the monitor and is not going to
// call park() for the remainder of the monitor enter protocol. So
// it doesn't matter if the JVMTI_EVENT_MONITOR_CONTENDED_ENTERED
// event handler consumed an unpark() issued by the thread that
// just exited the monitor.
}
if (event.should_commit()) {
event.set_previousOwner((uintptr_t)_previous_owner_tid);
event.commit();
}
if (ObjectMonitor::_sync_ContendedLockAttempts != NULL) {
ObjectMonitor::_sync_ContendedLockAttempts->inc() ;
}
}
这里是自旋的代码TrySpin (Self),在这之前我们需要了解一些相关的参数.
在这个方法中,我们只需要知道重试的次数是动态变化的,大概会在一个上下文切换之间进行浮动变化。次数根据最近获取到锁成功率进行相应的调整。在自旋的过程中,可能会暂停一小段时间调用SpinPause()阻塞当前线程,之后在进行自旋。而这个方法最后调用poll(NULL, 0, 100);方法,进行线程的阻塞。
int ObjectMonitor::Knob_SpinLimit = 5000 ; //自旋次数的上限值
static int Knob_SpinBase = 0 ; // Floor AKA SpinMin
static int Knob_SpinBackOff = 0 ; // spin-loop backoff
static int Knob_CASPenalty = -1 ; // CAS失败的惩罚
static int Knob_OXPenalty = -1 ; // Penalty for observed _owner change
static int Knob_SpinSetSucc = 1 ; // spinners set the _succ field
static int Knob_SpinEarly = 1 ;
static int Knob_SuccEnabled = 1 ; // futile wake throttling
static int Knob_SuccRestrict = 0 ; // Limit successors + spinners to at-most-one
static int Knob_MaxSpinners = -1 ; // CPU的数量
static int Knob_Bonus = 100 ; // spin success bonus
static int Knob_BonusB = 100 ; // 自旋成功后会增加一定的时间 spin success bonus
static int Knob_Penalty = 200 ; // spin failure penalty
static int Knob_Poverty = 1000 ; //自旋次数的下限值
static int Knob_SpinAfterFutile = 1 ; // Spin after returning from park()
static int Knob_FixedSpin = 0 ; //固定的自旋次数,不作为真正的使用,仅用来对比
static int Knob_OState = 3 ; // Spinner checks thread state of _owner
static int Knob_UsePause = 1 ;
static int Knob_ExitPolicy = 0 ;
static int Knob_PreSpin = 10 ; // 这里时预先自旋的次数,在真正消耗_SpinDuration之前,会进行自旋的次数。20-100 likely better
volatile int _Spinner ; // for exit->spinner handoff optimization
volatile int _SpinFreq ; // Spin 1-out-of-N attempts: success rate
volatile int _SpinClock ;
volatile int _SpinDuration ; //这个表示自旋的时间,在初始化时会设置为和Knob_SpinLimit一样的值,5000。这个值并不是固定的,会根据最近自旋的成功率而进行上下的浮动。
//不过总的值大概是一个上下文切换的时间.
volatile intptr_t _SpinState ; // MCS/CLH list of spinners
#define TrySpin TrySpin_VaryDuration
int ObjectMonitor::TrySpin_VaryDuration (Thread * Self) {
//固定自旋一定的测试,由于Knob_FixedSpin为0,所以这段代码在不参与实际运行,仅作为对比测试
int ctr = Knob_FixedSpin ;
if (ctr != 0) {
while (--ctr >= 0) {
//TryLock方法尝试获取锁
//如果_owner不为空,返回0,表示已经被别的线程持有
//如果为空,进行cas,成功返回1,表示获取到锁,接着退出该方法
//失败返回-1。这里要注意的是,这种情况下,锁被其他cas成功的线程持有了,那么进行自旋是没有必要的
if (TryLock (Self) > 0) return 1 ;
//以poll(NULL, 0, 100);的方式暂停当前线程
SpinPause () ;
}
return 0 ;
}
//Knob_PreSpin默认为10,_SpinDuration在inflate方法中被初始化为5000,与Knob_SpinLimit相同
for (ctr = Knob_PreSpin + 1; --ctr >= 0 ; ) {
//尝试获取锁,成功返回1
if (TryLock(Self) > 0) {
//这里如果_SpinDuration 自旋的次数减少到某个阈值,会适当的增加
//_SpinDuration并不能很精准的控制自旋的次数
//如果自旋成功了_SpinDuration会相应的进行增加,如果低于最低值,会被重新赋予最低值。
int x = _SpinDuration ;
if (x < Knob_SpinLimit) {
//Knob_Poverty默认值为1000
if (x < Knob_Poverty) x = Knob_Poverty ;
_SpinDuration = x + Knob_BonusB ;
}
return 1 ;
}
//以poll(NULL, 0, 100);的方式暂停当前线程
SpinPause () ;
}
//这里确保_SpinDuration不是小于0的
ctr = _SpinDuration ;
if (ctr < Knob_SpinBase) ctr = Knob_SpinBase ;
if (ctr <= 0) return 0 ;
if (Knob_SuccRestrict && _succ != NULL) return 0 ;
//NotRunnable用于判断获取到锁的线程是否处于运行状态,因为只有正在运行的线程在执行完之后才会释放锁
//而例如阻塞的线程那么就没有必要自旋等待锁了
//这里获取到线程的状态可能时错误的,因为获取到锁的线程可能已经死亡,返回值仅作为参考
if (Knob_OState && NotRunnable (Self, (Thread *) _owner)) {
TEVENT (Spin abort - notrunnable [TOP]);
return 0 ;
}
//Knob_MaxSpinners为CPU的数量,如果正在自旋的数量大于CPU的数量,说明太多尝试自旋的线程。
int MaxSpin = Knob_MaxSpinners ;
if (MaxSpin >= 0) {
if (_Spinner > MaxSpin) {
TEVENT (Spin abort -- too many spinners) ;
return 0 ;
}
// Slighty racy, but benign ...
Adjust (&_Spinner, 1) ;
}
int hits = 0 ;
int msk = 0 ;
int caspty = Knob_CASPenalty ;
int oxpty = Knob_OXPenalty ;
int sss = Knob_SpinSetSucc ;
if (sss && _succ == NULL ) _succ = Self ;
Thread * prv = NULL ;
while (--ctr >= 0) {
if ((ctr & 0xFF) == 0) {
//如果不是在安全点执行该操作,这个操作会被中止
if (SafepointSynchronize::do_call_back()) {
TEVENT (Spin: safepoint) ;
goto Abort ; // abrupt spin egress
}
if (Knob_UsePause & 1) SpinPause () ;
int (*scb)(intptr_t,int) = SpinCallbackFunction ;
if (hits > 50 && scb != NULL) {
int abend = (*scb)(SpinCallbackArgument, 0) ;
}
}
if (Knob_UsePause & 2) SpinPause() ;
if (ctr & msk) continue ;
++hits ;
if ((hits & 0xF) == 0) {
// The 0xF, above, corresponds to the exponent.
// Consider: (msk+1)|msk
msk = ((msk << 2)|3) & BackOffMask ;
}
//这里是获取锁的操作,如果_owner为NULL就会尝试获取锁。
Thread * ox = (Thread *) _owner ;
if (ox == NULL) {
ox = (Thread *) Atomic::cmpxchg_ptr (Self, &_owner, NULL) ;
if (ox == NULL) {
//成功获取锁
if (sss && _succ == Self) {
_succ = NULL ;
}
if (MaxSpin > 0) Adjust (&_Spinner, -1) ;
//自旋成功的奖励
int x = _SpinDuration ;
if (x < Knob_SpinLimit) {
if (x < Knob_Poverty) x = Knob_Poverty ;
_SpinDuration = x + Knob_Bonus ;
}
return 1 ;
}
//获取锁失败,会根据情况是重试还是退出。如果失败的概率很高,就会直接退出。
prv = ox ;
TEVENT (Spin: cas failed) ;
//退出,进行惩罚
if (caspty == -2) break ;
//退出,直接终止
if (caspty == -1) goto Abort ;
//重试
ctr -= caspty ;
continue ;
}
// Did lock ownership change hands ?
if (ox != prv && prv != NULL ) {
TEVENT (spin: Owner changed)
if (oxpty == -2) break ;
if (oxpty == -1) goto Abort ;
ctr -= oxpty ;
}
prv = ox ;
if (Knob_OState && NotRunnable (Self, ox)) {
TEVENT (Spin abort - notrunnable);
goto Abort ;
}
if (sss && _succ == NULL ) _succ = Self ;
}
//自旋失败,则会进行相应的惩罚
TEVENT (Spin failure) ;
{
int x = _SpinDuration ;
if (x > 0) {
// Consider an AIMD scheme like: x -= (x >> 3) + 100
// This is globally sample and tends to damp the response.
x -= Knob_Penalty ;
if (x < 0) x = 0 ;
_SpinDuration = x ;
}
}
Abort:
if (MaxSpin >= 0) Adjust (&_Spinner, -1) ;
if (sss && _succ == Self) {
_succ = NULL ;
OrderAccess::fence() ;
if (TryLock(Self) > 0) return 1 ;
}
return 0 ;
}
如果自旋一段时间还是获取不到锁,就会调用EnterI方法,这个方法同样能尝试获取锁,如果不成功就阻塞该线程。
void ATTR ObjectMonitor::EnterI (TRAPS) {
Thread * Self = THREAD ;
assert (Self->is_Java_thread(), "invariant") ;
assert (((JavaThread *) Self)->thread_state() == _thread_blocked , "invariant") ;
// 尝试获取锁
if (TryLock (Self) > 0) {
assert (_succ != Self , "invariant") ;
assert (_owner == Self , "invariant") ;
assert (_Responsible != Self , "invariant") ;
return ;
}
DeferredInitialize () ;
//尝试自旋
if (TrySpin (Self) > 0) {
assert (_owner == Self , "invariant") ;
assert (_succ != Self , "invariant") ;
assert (_Responsible != Self , "invariant") ;
return ;
}
assert (_succ != Self , "invariant") ;
assert (_owner != Self , "invariant") ;
assert (_Responsible != Self , "invariant") ;
//获取锁失败的情况,接下来要将线程挂起
//会将当前节点包装成为一个node,存放在_cxq队列中
// ObjectWaiter * volatile _cxq ; 这个队列用来存放最近阻塞的线程
ObjectWaiter * nxt ;
for (;;) {
//尝试将该节点放在_cxq队列的队头,成功跳出循环
node._next = nxt = _cxq ;
if (Atomic::cmpxchg_ptr (&node, &_cxq, nxt) == nxt) break ;
//失败继续尝试获取锁,这里失败自旋的原因是降低插入到队列中的频率
//这里在入队之前会不断的进行自旋尝试获取锁,而不是先从队列中唤醒线程
//所以synchronized是一个非公平锁。
if (TryLock (Self) > 0) {
assert (_succ != Self , "invariant") ;
assert (_owner == Self , "invariant") ;
assert (_Responsible != Self , "invariant") ;
return ;
}
}
//这段代码是设置一个负责人,如果所有的线程都如对阻塞了,那么谁来唤醒呢.
//负责人在进行阻塞时,只会阻塞有限的一段时间,就会被唤醒,再次尝试获取锁。
//如果这个负责人成功获取到了锁,那么Responsible会被置为空。
//新的负责人会由新来的线程担任,这时为了防止队列中的线程没有线程"醒来"而出现的"搁浅".
//那么,如果没有新的线程来并不是说这个队列就不会有线程被唤醒.
//获取到锁的线程在释放锁的时候,就会唤醒它的继承者_succ线程
if ((SyncFlags & 16) == 0 && nxt == NULL && _EntryList == NULL) {
// Try to assume the role of responsible thread for the monitor.
// CONSIDER: ST vs CAS vs { if (Responsible==null) Responsible=Self }
Atomic::cmpxchg_ptr (Self, &_Responsible, NULL) ;
}
TEVENT (Inflated enter - Contention) ;
int nWakeups = 0 ;
int RecheckInterval = 1 ;
for (;;) {
//尝试获取锁
if (TryLock (Self) > 0) break ;
assert (_owner != Self, "invariant") ;
//设置负责人
if ((SyncFlags & 2) && _Responsible == NULL) {
Atomic::cmpxchg_ptr (Self, &_Responsible, NULL) ;
}
// 阻塞线程,如果时负责人,阻塞一段时间,不是则一直阻塞
if (_Responsible == Self || (SyncFlags & 1)) {
TEVENT (Inflated enter - park TIMED) ;
Self->_ParkEvent->park ((jlong) RecheckInterval) ;
// Increase the RecheckInterval, but clamp the value.
RecheckInterval *= 8 ;
if (RecheckInterval > 1000) RecheckInterval = 1000 ;
} else {
TEVENT (Inflated enter - park UNTIMED) ;
Self->_ParkEvent->park() ;
}
//这里可能是负责人定时醒来,也可能是虚假唤醒
//都尝试获取锁
if (TryLock(Self) > 0) break ;
TEVENT (Inflated enter - Futile wakeup) ;
if (ObjectMonitor::_sync_FutileWakeups != NULL) {
ObjectMonitor::_sync_FutileWakeups->inc() ;
}
++ nWakeups ;
//如果不是虚假唤醒,那么一般succ == self
if ((Knob_SpinAfterFutile & 1) && TrySpin (Self) > 0) break ;
if ((Knob_ResetEvent & 1) && Self->_ParkEvent->fired()) {
Self->_ParkEvent->reset() ;
OrderAccess::fence() ;
}
if (_succ == Self) _succ = NULL ;
//内存屏障,防止指令重排序
OrderAccess::fence() ;
//继续for循环
}
assert (_owner == Self , "invariant") ;
assert (object() != NULL , "invariant") ;
// I'd like to write:
// guarantee (((oop)(object()))->mark() == markOopDesc::encode(this), "invariant") ;
// but as we're at a safepoint that's not safe.
UnlinkAfterAcquire (Self, &node) ;
if (_succ == Self) _succ = NULL ;
assert (_succ != Self, "invariant") ;
if (_Responsible == Self) {
_Responsible = NULL ;
//内存屏障,防止指令重排序
OrderAccess::fence(); // Dekker pivot-point
}
if (SyncFlags & 8) {
//内存屏障,防止指令重排序
OrderAccess::fence() ;
}
return ;
}
加锁小结
- 重量锁的锁头与轻量级锁和偏向锁是不同。轻量级锁和偏向锁使用的是basicLock且位置存储在本地的线程栈,而重量级锁使用的是ObjectMointer,本地和全局都有存储。会优先从本地进行获取,如果本地和全局都没有空闲的监视器,才会去创建.
- 与偏向锁和轻量级锁不同的是,重量锁会有一个创建中的一个状态。
- 在轻量级获取锁或者释放锁CAS操作的过程中如果出现了竞争,会先将锁膨胀为重量级。
- 与ReenTrantLcok类似,重量级锁也有一个队列用来存放挂起的线程,且同样使用park的方式来阻塞线程。
- 在线程加入阻塞队列之前会进行多次的自旋,时间大概是一个上下文。
- 如果正在自旋的数量大于CPU的数量,说明太多尝试自旋的线程,会直接返回。
- 重量级锁使用继承人和负责人机制来保证当前持有锁的线程释放锁后,等待的线程能被唤醒持有锁。继承人指的是当前持有锁的线程在释放锁时,会指定一个线程为下一个持有锁的线程,但是锁可能被抢占。负责人是阻塞队列中定时自我唤醒的线程,防止所有线程都在队列中阻塞而有没有新线程来竞争锁而导致的“搁浅”(stranding)。
重量级锁的释放
void ATTR ObjectMonitor::exit(bool not_suspended, TRAPS) {
Thread * Self = THREAD ;
if (THREAD != _owner) {
// 当前线程是之前持有轻量级锁膨胀成重量级锁,但是还没调用过enter方法,_owner还是指向Lock Record。
if (THREAD->is_lock_owned((address) _owner)) {
assert (_recursions == 0, "invariant") ;
_owner = THREAD ;
_recursions = 0 ;
OwnerIsThread = 1 ;
} else {
//异常情况
TEVENT (Exit - Throw IMSX) ;
assert(false, "Non-balanced monitor enter/exit!");
if (false) {
THROW(vmSymbols::java_lang_IllegalMonitorStateException());
}
return;
}
}
//接下来持有锁的线程释放锁,先判断重入的次数
if (_recursions != 0) {
_recursions--; // this is simple recursive enter
TEVENT (Inflated exit - recursive) ;
return ;
}
//将负责人设置为空
if ((SyncFlags & 4) == 0) {
_Responsible = NULL ;
}
#if INCLUDE_JFR
// get the owner's thread id for the MonitorEnter event
// if it is enabled and the thread isn't suspended
if (not_suspended && EventJavaMonitorEnter::is_enabled()) {
_previous_owner_tid = JFR_THREAD_ID(Self);
}
#endif
for (;;) {
assert (THREAD == _owner, "invariant") ;
//Knob_ExitPolicy默认为0
if (Knob_ExitPolicy == 0) {
//优先释放锁
OrderAccess::release_store_ptr (&_owner, NULL) ; // drop the lock
//判断是否有继承人,如果有就释放锁,直接退出。
//没有要重新获取锁,再设置继承人
OrderAccess::storeload() ; // See if we need to wake a successor
if ((intptr_t(_EntryList)|intptr_t(_cxq)) == 0 || _succ != NULL) {
TEVENT (Inflated exit - simple egress) ;
return ;
}
TEVENT (Inflated exit - complex egress) ;
//如果其他后续线程已经准备好或者其他线程正在自旋,那么这个线程可以简单地将owner设置为null中并退出而不唤醒后续线程。
//cas重新获取锁,失败就说明有其他线程在获取锁了,不需要设置继承者,return
if (Atomic::cmpxchg_ptr (THREAD, &_owner, NULL) != NULL) {
return ;
}
TEVENT (Exit - Reacquired) ;
} else {
//Knob_ExitPolicy不为0的情况
//优先观察队列和继承者是不是空的
if ((intptr_t(_EntryList)|intptr_t(_cxq)) == 0 || _succ != NULL) {
//释放锁
OrderAccess::release_store_ptr (&_owner, NULL) ; // drop the lock
OrderAccess::storeload() ;
if (_cxq == NULL || _succ != NULL) {
//直接退出
TEVENT (Inflated exit - simple egress) ;
return ;
}
//尝试直接取获取锁,如果失败直接返回
if (Atomic::cmpxchg_ptr (THREAD, &_owner, NULL) != NULL) {
TEVENT (Inflated exit - reacquired succeeded) ;
return ;
}
//成功就要继续执行后续的代码,设置继承者
TEVENT (Inflated exit - reacquired failed) ;
} else {
//继承者为空或者队列不为空
TEVENT (Inflated exit - complex egress) ;
}
}
guarantee (_owner == THREAD, "invariant") ;
ObjectWaiter * w = NULL ;
int QMode = Knob_QMode ;
//这里是不同的唤醒方式,默认为0
//方式2:直接从cxq队列中的队首唤醒继承者
if (QMode == 2 && _cxq != NULL) {
w = _cxq ;
assert (w != NULL, "invariant") ;
assert (w->TState == ObjectWaiter::TS_CXQ, "Invariant") ;
ExitEpilog (Self, w) ;
return ;
}
//方式3:将_cxq中的元素插入到_EntryList的队尾
if (QMode == 3 && _cxq != NULL) {
// Aggressively drain cxq into EntryList at the first opportunity.
// This policy ensure that recently-run threads live at the head of EntryList.
// Drain _cxq into EntryList - bulk transfer.
// First, detach _cxq.
// The following loop is tantamount to: w = swap (&cxq, NULL)
w = _cxq ;
for (;;) {
assert (w != NULL, "Invariant") ;
ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL, &_cxq, w) ;
if (u == w) break ;
w = u ;
}
assert (w != NULL , "invariant") ;
ObjectWaiter * q = NULL ;
ObjectWaiter * p ;
for (p = w ; p != NULL ; p = p->_next) {
guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ;
p->TState = ObjectWaiter::TS_ENTER ;
p->_prev = q ;
q = p ;
}
// Append the RATs to the EntryList
// TODO: organize EntryList as a CDLL so we can locate the tail in constant-time.
ObjectWaiter * Tail ;
for (Tail = _EntryList ; Tail != NULL && Tail->_next != NULL ; Tail = Tail->_next) ;
if (Tail == NULL) {
_EntryList = w ;
} else {
Tail->_next = w ;
w->_prev = Tail ;
}
// Fall thru into code that tries to wake a successor from EntryList
}
//方式4:将_cxq中的元素插入到_EntryList的队首,如果_EntryList不为空,唤醒队首
if (QMode == 4 && _cxq != NULL) {
// Aggressively drain cxq into EntryList at the first opportunity.
// This policy ensure that recently-run threads live at the head of EntryList.
// Drain _cxq into EntryList - bulk transfer.
// First, detach _cxq.
// The following loop is tantamount to: w = swap (&cxq, NULL)
w = _cxq ;
for (;;) {
assert (w != NULL, "Invariant") ;
ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL, &_cxq, w) ;
if (u == w) break ;
w = u ;
}
assert (w != NULL , "invariant") ;
ObjectWaiter * q = NULL ;
ObjectWaiter * p ;
for (p = w ; p != NULL ; p = p->_next) {
guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ;
p->TState = ObjectWaiter::TS_ENTER ;
p->_prev = q ;
q = p ;
}
// Prepend the RATs to the EntryList
if (_EntryList != NULL) {
q->_next = _EntryList ;
_EntryList->_prev = q ;
}
_EntryList = w ;
// Fall thru into code that tries to wake a successor from EntryList
}
w = _EntryList ;
if (w != NULL) {
// I'd like to write: guarantee (w->_thread != Self).
// But in practice an exiting thread may find itself on the EntryList.
// Lets say thread T1 calls O.wait(). Wait() enqueues T1 on O's waitset and
// then calls exit(). Exit release the lock by setting O._owner to NULL.
// Lets say T1 then stalls. T2 acquires O and calls O.notify(). The
// notify() operation moves T1 from O's waitset to O's EntryList. T2 then
// release the lock "O". T2 resumes immediately after the ST of null into
// _owner, above. T2 notices that the EntryList is populated, so it
// reacquires the lock and then finds itself on the EntryList.
// Given all that, we have to tolerate the circumstance where "w" is
// associated with Self.
assert (w->TState == ObjectWaiter::TS_ENTER, "invariant") ;
//这个方法会设置继承者,并使用继承者区竞争锁(可能失败)
ExitEpilog (Self, w) ;
return ;
}
// If we find that both _cxq and EntryList are null then just
// re-run the exit protocol from the top.
w = _cxq ;
if (w == NULL) continue ;
// Drain _cxq into EntryList - bulk transfer.
// First, detach _cxq.
// The following loop is tantamount to: w = swap (&cxq, NULL)
for (;;) {
assert (w != NULL, "Invariant") ;
ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL, &_cxq, w) ;
if (u == w) break ;
w = u ;
}
TEVENT (Inflated exit - drain cxq into EntryList) ;
assert (w != NULL , "invariant") ;
assert (_EntryList == NULL , "invariant") ;
//方式1:将_cxq中的元素转移到_EntryList,并反转顺序
if (QMode == 1) {
// QMode == 1 : drain cxq to EntryList, reversing order
// We also reverse the order of the list.
ObjectWaiter * s = NULL ;
ObjectWaiter * t = w ;
ObjectWaiter * u = NULL ;
while (t != NULL) {
guarantee (t->TState == ObjectWaiter::TS_CXQ, "invariant") ;
t->TState = ObjectWaiter::TS_ENTER ;
u = t->_next ;
t->_prev = u ;
t->_next = s ;
s = t;
t = u ;
}
_EntryList = s ;
assert (s != NULL, "invariant") ;
} else {
//方式0或者方式2: 将_cxq中的元素转移到_EntryList,如果_EntryList不为空,唤醒第一个线程
_EntryList = w ;
ObjectWaiter * q = NULL ;
ObjectWaiter * p ;
for (p = w ; p != NULL ; p = p->_next) {
guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ;
p->TState = ObjectWaiter::TS_ENTER ;
p->_prev = q ;
q = p ;
}
}
// In 1-0 mode we need: ST EntryList; MEMBAR #storestore; ST _owner = NULL
// The MEMBAR is satisfied by the release_store() operation in ExitEpilog().
// See if we can abdicate to a spinner instead of waking a thread.
// A primary goal of the implementation is to reduce the
// context-switch rate.
if (_succ != NULL) continue;
w = _EntryList ;
if (w != NULL) {
guarantee (w->TState == ObjectWaiter::TS_ENTER, "invariant") ;
//这个方法会设置继承者,并使用继承者区竞争锁(可能失败)
ExitEpilog (Self, w) ;
return ;
}
}
}
重量锁释放小结
- 重量锁再释放的时候,如果等待队列不为空或者未指定继承人,那么需要指定继承人。
- 继承人也需要参与不在等待队列中的线程竞争锁,所以不是一定会获取到锁。