
今天我们来分享两道两道BT的多线程经典面试题,作者是黄俊老师。
黄俊老师,95后、23岁拿到阿里60w年薪,之后在美团拿到百万年薪,现在一心只想钻研技术。
由于文章篇幅较长,我们会把两个问题分开来讲,避免有小伙伴搞不懂,接下来我们先来看第一个问题:
线程唤醒问题
样例代码:
public class Test {/*** 有三个线程 A,B,C* A为什么总是在C前面抢到锁???*/private final static Object LOCK = new Object();public void startThreadA() {new Thread(() -> {synchronized (LOCK) {System.out.println(Thread.currentThread().getName() + ": get lock");//启动线程bstartThreadB();System.out.println(Thread.currentThread().getName() + ": start wait");try {//线程a waitLOCK.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + ": get lock after wait");System.out.println(Thread.currentThread().getName() + ": release lock");}}, "thread-A").start();}private void startThreadB() {new Thread(() -> {synchronized (LOCK) {System.out.println(Thread.currentThread().getName() + ": get lock");//启动线程cstartThreadC();try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + ": start notify");//线程b唤醒其他线程LOCK.notify();System.out.println(Thread.currentThread().getName() + ": release lock");}}, "thread-B").start();}private void startThreadC() {new Thread(() -> {System.out.println(Thread.currentThread().getName() + ": thread c start");synchronized (LOCK) {System.out.println(Thread.currentThread().getName() + ": get lock");System.out.println(Thread.currentThread().getName() + ": release lock");}}, "thread-C").start();}public static void main(String[] args) {new Test().startThreadA();}}
输出结果:
thread-A: get lockthread-A: start waitthread-B: get lockthread-C: thread c startthread-B: start notifythread-B: release lockthread-A: get lock after waitthread-A: release lockthread-C: get lockthread-C: release lock问题: 为什么每次运行,线程A总是优先于线程C获取锁?
分析:
线程竞争锁失败后CAS放入cxq列表中 线程释放锁后将根据策略来唤醒cxq或者entrylist中的线程(我们这里只讨论默认策略) 默认策略下优先唤醒entrylist列表中的线程,因为唤醒线程对象的操作是单线程的,也即只有获取锁并且释放锁的线程可以操作,所以操作entrylist是线程安全的 如果entrylist列表为空,那么将会CAS将cxq中的等待线程一次性获取到entrylist中并开始逐个唤醒
JVM_ENTRY(void, JVM_MonitorNotify(JNIEnv* env, jobject handle))Handle obj(THREAD, JNIHandles::resolve_non_null(handle));// 直接调用ObjectSynchronizer::notifyObjectSynchronizer::notify(obj, CHECK);JVM_END
void ObjectSynchronizer::notify(Handle obj, TRAPS) {if (UseBiasedLocking) {// 如果使用偏向锁,那么取消偏向锁BiasedLocking::revoke_and_rebias(obj, false, THREAD);}markOop mark = obj->mark();if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) {// 如果是轻量级锁,那么直接返回,因为wait操作需要通过对象监视器来做return;}ObjectSynchronizer::inflate(THREAD, obj())->notify(THREAD);}
void ObjectMonitor::notify(TRAPS) {CHECK_OWNER();if (_WaitSet == NULL) {// 如果等待集为空,直接返回return ;}int Policy = Knob_MoveNotifyee ; // 移动策略,这里默认是2Thread::SpinAcquire (&_WaitSetLock, "WaitSet - notify") ; // 首先对等待集上自旋锁// 调用DequeueWaiter将一个等待线程从等待集中拿出来ObjectWaiter * iterator = DequeueWaiter() ;if (iterator != NULL) {if (Policy != 4) { // 如果策略不等于4那么将线程的状态修改为TS_ENTERiterator->TState = ObjectWaiter::TS_ENTER ;}iterator->_notified = 1 ; // 唤醒计数器Thread * Self = THREAD;iterator->_notifier_tid = Self->osthread()->thread_id();ObjectWaiter * List = _EntryList ;if (Policy == 0) { // 如果策略为0,那么头插入到entrylist中if (List == NULL) { // 如果entrylist为空,那么将当前监视器直接作为_EntryList 头结点iterator->_next = iterator->_prev = NULL ;_EntryList = iterator ;} else { // 否则头插List->_prev = iterator ;iterator->_next = List ;iterator->_prev = NULL ;_EntryList = iterator ;}} else if (Policy == 1) { // 如果策略为1,那么插入entrylist的尾部if (List == NULL) {iterator->_next = iterator->_prev = NULL ;_EntryList = iterator ;} else {ObjectWaiter * Tail ;for (Tail = List ; Tail->_next != NULL ; Tail = Tail->_next) ;Tail->_next = iterator ;iterator->_prev = Tail ;iterator->_next = NULL ;}} else if (Policy == 2) {// 如果策略为2,那么如果entrylist为空,那么插入entrylist,否则插入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 ;}}}} elseif (Policy == 3) { // 如果策略为3,那么直接插入cxqiterator->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 {// 否则直接唤醒线程,让线程自己去调用enterI进入监视器ParkEvent * ev = iterator->_event ;iterator->TState = ObjectWaiter::TS_RUN ;OrderAccess::fence() ;ev->unpark() ;}}Thread::SpinRelease (&_WaitSetLock) ; // 释放等待集自旋锁}
inline ObjectWaiter* ObjectMonitor::DequeueWaiter() {ObjectWaiter* waiter = _WaitSet; // 很简单对吧,直接从头部拿if (waiter) { // 如果waiter不为空,那么从等待集中断链DequeueSpecificWaiter(waiter);}return waiter;}inline void ObjectMonitor::DequeueSpecificWaiter(ObjectWaiter* node) {ObjectWaiter* next = node->_next;if (next == node) { // 如果只有一个节点,那么直接将等待集清空即可_WaitSet = NULL;} else { // 否则双向链表的断链基础操作ObjectWaiter* prev = node->_prev;next->_prev = prev;prev->_next = next;if (_WaitSet == node) {_WaitSet = next;}}// 断开连接后,也需要把断下来的节点,next和prev指针清空node->_next = NULL;node->_prev = NULL;}
总结:

👇推荐关注👇
有趣的行业资讯
干货技术分享
程序员的日常生活
......

文章转载自马士兵,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




