synchronized在升级成重量锁之前有轻量级锁、偏向锁等,其中几种锁的转化、升级、触发、撤销等,有一套复杂的原理,后面我会出相关的专篇博客。今天先来打破粗略使用层面的印象,延续前面的博文,讲一下synchronized重量级锁的争用,释放,唤醒机制。对比ReentrantLock,看看他们设计的相同和不同之处。
synchronized的源码在jdk8/jdk8/hotspot: loghttp://hg.openjdk.java.net/jdk8/jdk8/hotspot/ 可以下载出来各种修改节点的版本源代码,但是太乱,也可以从 OpenJDK 下载
找hotspot:
synchronized(obj)实现的同步锁机制中,真正互斥的锁是monitor,在这个环境下翻译就是对象监视器,俗称管程,监视对象的状态和线程占用,争用,达到同步器的效果。
而我们平时所用的传参obj,其实不是锁对象,而是被锁的对象,也叫参照对象。只不过很多人喜欢命名为lock——锁对象(轻量级锁只需要这个obj作为存储锁状态,持有线程信息的参照资源,而且互斥锁对象monitor因他而生,这么说也没毛病,不过这里还是不要混淆),每个obj对象都可以产生一个monitor锁对象,那么找到相应的cpp源码文件objectMonitor.cpp:
下面贴出源码:
ObjectMonitor() {
_header = NULL; // 升级为重量级锁时,存储参照对象的带有hashcodede markword
_count = 0; // 记录获取该锁对象的线程数
_waiters = 0, // 等待中的线程数
_recursions = 0; // 线程重入次数
_object = NULL; // 存储该 monitor 监视的对象,就是参照对象
_owner = NULL; // 指向拥有该 monitor 的线程
_WaitSet = NULL; // 等待线程 双向循环链表_WaitSet 指向第一个节点
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ; // 多线程竞争锁时的单向链表
FreeNext = NULL ;
_EntryList = NULL ; // _owner 从该双向循环链表中唤醒线程,
_SpinFreq = 0 ; // 自旋频率,每次尝试自旋的次数
_SpinClock = 0 ; // 自旋时钟
OwnerIsThread = 0 ;
_previous_owner_tid = 0; // 前一个拥有此监视器的线程 ID
}
首先并不是所有的线程获取同步器的情况都会申请monitor,只有锁膨胀为重量级锁的时候才会创建monitor对象。
- _owner当有线程占有该 monitor 时 owner 标记为该线程的 ID。当线程释放 monitor 时 owner 恢复为 NULL。owner 是一个临界资源 JVM 是通过 CAS 操作来保证其线程安全的。
- _cxq:
- 竞争队列(Contention Queue)cxq 是虚拟的队列,是在于 cxq 是由 Node (将线程封装成ObjectWaiter对象node)及其 next 指针逻辑构成的,并不是队列数据结构(单向),在线程进入 cxq 时会通过 CAS 自旋获取锁,如果获取不到锁就会进入 cxq 队列。
- _cxq 是一个临界资源 ,同样JVM 也是通过 CAS 原子指令来修改_cxq 队列。jvm默认策略是(后面所有的机制都是以默认策略来说的):每一次加入新的 Node 会在 cxq 的队头进行,通过 CAS 改变第一个节点的指针指向新增的节点,CAS成功后设置 Node 的 next 节点指向之前的头结点,不成功一直循环执行CAS,和AQS的入队操作是一样的设计思想。获取元素也是从 cxq 队首获取元素。也就是每当有新来的节点入队,它的 next 指针总是指向之前队列的头节点,而_cxq 指针会指向该新入队的节点,所以是后进先出LIFO。
- _EntryList: 待入队列,可以理解为银行,医院排队叫号的预备号队列,路口左转待转区,不同于cxq,EntryList队列里线程是最有资格被唤醒阻塞。它是FIFO先进先出,即:尾进头出。除了cxq队列里面的线程到EntryList,notify()唤醒,有时会将waitSet里面的线程直接放到EntryList.
- _WaitSet: 等待队列因为调用 wait 方法而被阻塞的线程会被放在该队列中。一个锁参照对象只能有一个Monitor锁对象,所以也只有一个WaitSet。
- recursions:线程重入次数。
执行 monitorenter 指令时 调用以下代码:
IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem))
#ifdef ASSERT
thread->last_frame().interpreter_frame_verify_monitor(elem);
#endif
if (PrintBiasedLockingStatistics) {
Atomic::inc(BiasedLocking::slow_path_entry_count_addr());
}
Handle h_obj(thread, elem->obj());
assert(Universe::heap()->is_in_reserved_or_null(h_obj()),"must be NULL or an object");
// 是否使用偏向锁 JVM 启动时设置的偏向锁-XX:-UseBiasedLocking=false/true
if (UseBiasedLocking) {
// Retry fast entry if bias is revoked to avoid unnecessary inflation
ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK);
} else {
// 不设置偏向锁会进入轻量级锁的逻辑
ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK);
}
assert(Universe::heap()->is_in_reserved_or_null(elem->obj()),
"must be NULL or an object");
#ifdef ASSERT
thread->last_frame().interpreter_frame_verify_monitor(elem);
#endif
IRT_END
slow_enter 方法主要是轻量级锁的一些操作,如果操作失败则会膨胀为重量级锁,enter 方法则为重量级锁的入口源码如下:
void ATTR ObjectMonitor::enter(TRAPS) {
Thread * const Self = THREAD ;
void * cur ;
// 省略部分代码
/**
通过 CAS 操作尝试把 monitor 的 _owner 字段设置为当前线程
在ObjectMonitor::enter进入的时候会调用Actomic当中的cmpxchg_ptr;
Atomic::cmpxchg_ptr(Self, &_owner, NULL)
该函数属于linux内核系统中的函数,依赖cpu去做原子操作
CAS是一个原子的赋值操作;
作用就是将monitor对象当中的_owner设置成这个当前线程Self;
*/
// 通过 CAS 操作尝试把 monitor 的_owner 字段设置为当前线程
cur = Atomic::cmpxchg_ptr (Self, &_owner, NULL) ;
if (cur == NULL) {
assert (_recursions == 0 , "invariant") ;
assert (_owner == Self, "invariant") ;
return ;
}
// 线程重入,recursions++ 如果当前线程的_owner时当前线程,锁计数+1,表明进入了新的拥有相同锁的同步代码块
if (cur == Self) {
_recursions ++ ;
return ;
}
/**
如果当前线程第一次来抢monitor该锁;
如果当前线程是第一次进入该monitor,如果抢到锁了;
设置_recursions为1,并且将_owner设置为当前线程;
最后返回即表示当前线程竞争到该锁;
*/
// 如果当前线程是第一次进入该 monitor, 设置_recursions 为 1,_owner 为当前线程
if (Self->is_lock_owned ((address)cur)) {
assert (_recursions == 0, "internal state error");
_recursions = 1 ;
_owner = Self ;
OwnerIsThread = 1 ;
return ;
}
//以上操作都没有抢到锁,进入循环等待机会
for (;;) {
jt->set_suspend_equivalent();
// 如果获取锁失败,则等待锁的释放;
EnterI (THREAD) ;
if (!ExitSuspendEquivalent(jt)) break ;
_recursions = 0 ;
_succ = NULL ;
exit (false, Self) ;
jt->java_suspend_self();
}
Self->set_current_pending_monitor(NULL);
}
}
monitor等待
- 当前线程被封装成 ObjectWaiter 对象 node,状态设置成 ObjectWaiter::TS_CXQ。
- for 循环通过 CAS 把 node 节点 push 到_cxq列表中,同一时刻可能有多个线程把自己的 node 节点 push 到_cxq列表中。
- node 节点 push 到_cxq 列表之后,通过自旋尝试获取锁,如果还是没有获取到锁则通过 park 将当前线程挂起等待被唤醒。
- 当该线程被唤醒时会从挂起的点继续执行,通过 ObjectMonitor::TryLock 尝试获取锁。
// 省略部分代码
void ATTR ObjectMonitor::EnterI (TRAPS) {
Thread * Self = THREAD ;
assert (Self->is_Java_thread(), "invariant") ;
assert (((JavaThread *) Self)->thread_state() == _thread_blocked , "invariant") ;
// Try lock 尝试获取锁
if (TryLock (Self) > 0) {
assert (_succ != Self , "invariant") ;
assert (_owner == Self , "invariant") ;
assert (_Responsible != Self , "invariant") ;
// 如果获取成功则退出,避免 park unpark 系统调度的开销
return ;
}
// 自旋获取锁
if (TrySpin(Self) > 0) {
assert (_owner == Self, "invariant");
assert (_succ != Self, "invariant");
assert (_Responsible != Self, "invariant");
return;
}
// 当前线程被封装成 ObjectWaiter 对象 node, 状态设置成 ObjectWaiter::TS_CXQ
ObjectWaiter node(Self) ;
Self->_ParkEvent->reset() ;
node._prev = (ObjectWaiter *) 0xBAD ;
node.TState = ObjectWaiter::TS_CXQ ;
// 通过 CAS 把 node 节点 push 到_cxq 列表中
ObjectWaiter * nxt ;
//期间可能会有失败,循环将所有没抢到锁的线程都放入cxq中
for (;;) {
// node节点插在头部,置换为_cxq队列的头节点
node._next = nxt = _cxq ;
if (Atomic::cmpxchg_ptr (&node, &_cxq, nxt) == nxt) break ;
// Interference - the CAS failed because _cxq changed. Just retry.
// As an optional optimization we retry the lock.
//在放入cxq的期间尝试再次获取锁,基本和AQS逻辑一样的。AQS应该是就是仿照jvm写的
if (TryLock (Self) > 0) {
assert (_succ != Self , "invariant") ;
assert (_owner == Self , "invariant") ;
assert (_Responsible != Self , "invariant") ;
return ;
}
}
for (;;) {
// 本段代码的主要思想和 AQS 中相似可以类比来看
// 线程在挂起前做一下挣扎,看能不能获取到锁
if (TryLock (Self) > 0) break ;
assert (_owner != Self, "invariant") ;
if ((SyncFlags & 2) && _Responsible == NULL) {
Atomic::cmpxchg_ptr (Self, &_Responsible, NULL) ;
}
// 满足条件则 park self
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) ;
// 通过 park 将当前线程挂起,等待被唤醒
Self->_ParkEvent->park() ;
}
if (TryLock(Self) > 0) break ;
// 再次尝试自旋
if ((Knob_SpinAfterFutile & 1) && TrySpin(Self) > 0) break;
}
return ;
}
该线程被唤醒时,会从挂起的点继续执行,通过ObjectMonitor::TryLock尝试获取锁,TryLock方法实现如下:
int ObjectMonitor::TryLock(Thread * Self){
for(;;){
void * own = owner;
if(own != null) return 0;
if(Atomic::cmpxchg_ptr (Self, &owner, NULL) == NULL){
// Either guarantee recursions == 0 or set _recursions = 0.
assert (recursions == 0, "invariant");
assert (_owner == Self, "invariant");
// CONSIDER: set or assert that OwnerIsThread == 1
return 1;
}
// The lock had been freen momentarily, but we lost the race to the lock.
// Interference -- the CAS faild.
// we can either return -1 or retry.
// Retry doesn't make as much sense because the lock was just acquired.
if(true) return -1;
}
}
当某个持有锁的线程执行完同步代码块时,会释放锁并 unpark 后续线程(由于篇幅只保留重要代码)。
下面是openJdk中阻塞线程被唤醒阻塞的源代码:
void ATTR ObjectMonitor::exit(bool not_suspended, TRAPS) {
Thread * Self = THREAD ;
if (_recursions != 0) {
_recursions--; // this is simple recursive enter
TEVENT (Inflated exit - recursive) ;
return ;
}
ObjectWaiter * w = NULL ;
int QMode = Knob_QMode ;
// 直接绕过 EntryList 队列,从 cxq 队列中获取线程用于竞争锁
if (QMode == 2 && _cxq != NULL) {
w = _cxq ;
assert (w != NULL, "invariant") ;
assert (w->TState == ObjectWaiter::TS_CXQ, "Invariant") ;
ExitEpilog (Self, w) ;
return ;
}
// cxq 队列插入 EntryList 尾部
if (QMode == 3 && _cxq != NULL) {
w = _cxq ;
// _cxq指向null
for (;;) {
assert (w != NULL, "Invariant") ;
ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL, &_cxq, w) ;
if (u == w) break ;
w = u ;
}
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 ;
}
ObjectWaiter * Tail ;
for (Tail = _EntryList ; Tail != NULL && Tail->_next != NULL ; Tail = Tail->_next) ;
// 放到entryList列表尾部
//如果EntryList为空,直接全量放入到EntryList==赋值给EntryList
if (Tail == NULL) {
_EntryList = w ;
} else {
// EntryList不为空,直接放入尾部
Tail->_next = w ;
w->_prev = Tail ;
}
}
// cxq 队列插入到_EntryList 头部
if (QMode == 4 && _cxq != NULL) {
// 把 cxq 队列放入 EntryList
// 此策略确保最近运行的线程位于 EntryList 的头部
w = _cxq ;
// _cxq指向null
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 ;
}
// 放入头部:直接将节点的next指向EntryList
if (_EntryList != NULL) {
q->_next = _EntryList ;
_EntryList->_prev = q ;
}
//同样 EntryList为空,直接将转化后的cxq赋值给EntryList==全量放入到EntryList
_EntryList = w ;
}
w = _EntryList ;
if (w != NULL) {
assert (w->TState == ObjectWaiter::TS_ENTER, "invariant") ;
ExitEpilog (Self, w) ;
return ;
}
w = _cxq ;
if (w == NULL) continue ;
//
for (;;) {
assert (w != NULL, "Invariant") ;
ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL, &_cxq, w) ;
if (u == w) break ;
w = u ;
}
if (QMode == 1) {
// QMode == 1 : 把 cxq 倾倒入 EntryList 逆序
// 单向链表改双向,且逆序。s代表前一个节点,u代表下一个节点
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必为空
_EntryList = s ;
assert (s != NULL, "invariant") ;
} else {
// QMode == 0 默认值,其实entryList== null 最终逻辑和 QMode == 2 QMode == 3
//是一样的
// 先将cxq全量放入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 ;
}
}
// 不为空,说明已经唤醒恢复成功
if (_succ != NULL) continue;
w = _EntryList ;
if (w != NULL) {
guarantee (w->TState == ObjectWaiter::TS_ENTER, "invariant") ;
ExitEpilog (Self, w) ;
return ;
}
}
}
退出同步块的策略:
如果没有线程获取锁成功,需要唤醒队列中的节点
- Qmode == 2 && CXQ!=null优先从CXQ队列中唤醒线程,从头部开始唤醒
- Qmode == 3 && CXQ!=null将CXQ队列尾插法插入到EntryList
- Qmode == 4 && CXQ!=null将CXQ队列头插法插入到EntryList
- 完成以上三步的操作之后(Qmode 默认值 0)
- 如果CXQ==null,EntryList!=null,从EntryList队列中唤醒
- 如果CXQ==null EntryList==null,continue执行
先将Monitor的owner设置为null,表示完全释放了锁对象(这时如果有其他线程进入同步块则能获得锁),相当于退出同步代码块。
- 只有EntryList等于null的时候,而且Cxq不为空的时候才会执行下面的操作
- Qmode == 1 将倒置CXQ队列,加入到EntryList中,然后从EntryList中唤醒线程
- Qmode!=1 将CXQ队列,加入到EntryList中,然后从EntryList中唤醒线程
- 其他的没说倒置,都是按照cxq的头尾顺序,也就是和cxq的顺序一致
唤醒过程总结为:
1、 退出同步代码块时 会让_recursions减1,当_recursions的值减为0时,说明(线程完全退出了同步代码块中)线程释放了锁;
2、 (释放完锁之后需要唤醒线程)根据不同的策略(策略不同唤醒不同的线程)(由QMode指定),从_cxq或_EntryList中获取头节点,通过ObjectMonitor::ExitEpilog方法唤醒该节点封装的线程,唤醒操作最终由unpack完成(将之前park的线程进行唤醒),实现如下
-----------------------------------------------------------------
# 截取部分代码
void ObjectMonitor::ExitEpilog(Thread * Self, ObjectWaiter * wakee){
assert( _owner == Self, "invariant");
_succ = Knob_SuccEnabled ? wakee->_thread : NULL;
ParkEvent * Trigger = wakee->_event;
wakee = NULL;
// Drop the lock
OrderAccess::release_store_ptr(&_owner, NULL);
OrderAccess::fence(); // ST _owner vs LD in unpark()
if(SafepointSynchronize::do_call_back()){
TEVENT(unpack before SAFEPOINT);
}
DTRACE_MONITOR_PROBE(contended__exit, this, object(), Self);
Trigger->unpark(); // 唤醒之前被park()挂起的线程
// Maintain stats and report events to JVMTI
if (ObjectMonitor::_synch_Parks != NULL){
ObjectMonitor::_sync_Parks->inc();
}
}
notify唤醒
notify 或者 notifyAll 方法可以唤醒同一个锁监视器下调用 wait 挂起的线程,具体实现如下:
void ObjectMonitor::notify(TRAPS) {
CHECK_OWNER();
if (_WaitSet == NULL) {
TEVENT (Empty - Notify);
return;
}
DTRACE_MONITOR_PROBE(notify, this, object(), THREAD);
int Policy = Knob_MoveNotifyee;
Thread::SpinAcquire(&_WaitSetLock, "WaitSet - notify");
ObjectWaiter *iterator = DequeueWaiter();
if (iterator != NULL) {
// 省略一些代码
// 头插 EntryList
if (Policy == 0) {
if (List == NULL) {
iterator->_next = iterator->_prev = NULL;
_EntryList = iterator;
} else {
List->_prev = iterator;
iterator->_next = List;
iterator->_prev = NULL;
_EntryList = iterator;
}
} else if (Policy == 1) { // 尾插 EntryList
if (List == NULL) {
iterator->_next = iterator->_prev = NULL;
_EntryList = iterator;
} else {
ObjectWaiter *Tail;
for (Tail = List; Tail->_next != NULL; Tail = Tail->_next);
assert (Tail != NULL && Tail->_next == NULL, "invariant");
Tail->_next = iterator;
iterator->_prev = Tail;
iterator->_next = NULL;
}
} else if (Policy == 2) { // 头插 cxq
// prepend to cxq
if (List == NULL) {
iterator->_next = iterator->_prev = NULL;
_EntryList = iterator;
} else {
iterator->TState = ObjectWaiter::TS_CXQ;
for (;;) {
ObjectWaiter *Front = _cxq;
iterator->_next = Front;
if (Atomic::cmpxchg_ptr(iterator, &_cxq, Front) == Front) {
break;
}
}
}
} else if (Policy == 3) { // 尾插 cxq
iterator->TState = ObjectWaiter::TS_CXQ;
for (;;) {
ObjectWaiter *Tail;
Tail = _cxq;
if (Tail == NULL) {
iterator->_next = NULL;
if (Atomic::cmpxchg_ptr(iterator, &_cxq, NULL) == NULL) {
break;
}
} else {
while (Tail->_next != NULL) Tail = Tail->_next;
Tail->_next = iterator;
iterator->_prev = Tail;
iterator->_next = NULL;
break;
}
}
} else {
ParkEvent *ev = iterator->_event;
iterator->TState = ObjectWaiter::TS_RUN;
OrderAccess::fence();
ev->unpark();
}
if (Policy < 4) {
iterator->wait_reenter_begin(this);
}
}
// 自旋释放
Thread::SpinRelease(&_WaitSetLock);
if (iterator != NULL && ObjectMonitor::_sync_Notifications != NULL) {
ObjectMonitor::_sync_Notifications->inc();
}
}
notify的策略
如果调用了Object中的wait方法,那么节点会转移到waitSet队列中,只有再次调用notify方法 才能将waitSet队列中的头节点放入entryList或者cxq,notifyAll 全部节点加入到cxq中(EntryList头结点线程叫做 OnDeck Thread,notifyAll不管cxq中是否为空,都会将待唤醒的线程节点都放到cxq中),然后在由线程释放锁之后进行唤醒On Deck线程。至于是否能够抢到锁,需要看CAS是否成功,因为可能刚好有新建的线程也通过CAS抢占Owner
- Policy=0:将ObjectWaiter放入到enteylist队列的排头位置
- Policy=1:放入到entrylist队列末尾位置
- Policy=2(默认):判断entrylist是否为空,为空就放入到entrylist中,否则放入到cxq队列的排头位置
- Policy=3:判断cxq是否为空,如果为空,直接放入头部,否则放入cxq队列末尾位置
注意: notify方法并不一定能够立刻唤醒调用Wait的线程。
总体逻辑示意图:
从上述默认的策略搭配来看:synchronized-monitor实现的互斥锁是后来居上的策略,也就是最新进入争用队列cxq的线程,优先被唤醒阻塞,而被notify的线程,则一般是进入EntryList成为OnDeck线程,优先级更高。
当我们修改openJdk的源码再编译后,可以将QMode强制设置为1,policy设置为1或者保持默认的2,可以得到近似的公平锁机制,只是被notify的线程仍然具有更高唤醒优先级。
所以在可以修改策略情况下,synchronized-monitor实现的互斥锁也可以做成公平锁机制。
AQS中的公平锁:
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
// AQS 继承过来的方法, 方便阅读, 放在此处
public final void acquire(int arg) {
if (
!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
) {
selfInterrupt();
}
}
// 与非公平锁主要区别在于 tryAcquire 方法的实现
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 先检查 AQS 队列中是否有前驱节点, 没有才去竞争
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
// ㈠ AQS 继承过来的方法, 方便阅读, 放在此处
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
// h != t 时表示队列中有 Node
return h != t &&
(
// (s = h.next) == null 表示队列中还有没有老二
(s = h.next) == null || // 或者队列中老二线程不是此线程
s.thread != Thread.currentThread()
);
}
}
其实就是新建的线程,如果有线程阻塞在争用队列CLH中(有前驱节点),就没有CAS的机会了,但是并不是先启动的线程就可以先入队,CAS和CPU调度的原因,不是先尝试入队,就一定先入队的。也不是绝对的公平的机制。
更多资源分享,请关注我的公众号:搜索或扫码 砥砺code