Android线程锁机制:monitor机制解析
The following article is from 半行代码 Author 半行代码
最近打算了解下死锁监控,于是先探究了下java在Android art平台synchronized的原理,看了下相关的源码,这里分享一下相关的实现细节。synchronized在底层是通过moniter监视器来实现的,获取锁的时候,会生成一个monitor-enter指令,释放锁的时候会生成一个monitor-exit指令。monitor的相关实现在 monitor.h和 monitor.cc里面。
获取锁和锁升级过程
kUnlocked 无锁状态
当前是无锁状态,这里会通过cas升级到kThinLocked。
kThinLocked 轻量级锁
这里会把轻量级锁的数量加1,如果数量达到 kThinLockMaxCount 这个阈值,就会升级成重量级锁。如果不是当前线程持有这个锁,那就一直自旋尝试获取:
如果自旋次数 > kExtraSpinIters阈值,那么会执行 sched_yield,放弃争夺cpu。否则会升级为重量级锁。
kFatLocked 重量级
这里直接调用Monitor的Lock函数。
锁升级过程
如果不是当前线程,那么会暂停持有锁的线程,将他升级成重量级锁之后再恢复线程:
这个地方说明只要有一个线程持有了重量级锁,那么其他线程也会升级为重量级锁。Inflate函数里调用Install函数,根据持有锁的的对象的lockword状态判断:
轻量级锁
重量级锁
加锁过程
后面会使用mutex去加锁:
释放锁过程
无锁
这种是执行失败的情况。
轻量级锁
如果lockword存储的线程id不是当前线程,执行失败。exit的过程中轻量级锁会更新数量,每次减1。
重量级锁
重量级锁直接调用Unlock函数解锁。
解锁过程
这里如果是当前线程,会把lock_count减去1,当lock_count为0的时候,说明可以正式释放锁。调用SignalWaiterAndReleaseMonitorLock函数。这个函数后面再看。
wait
monitor里面定义了2个队列:
wait_set_ 等待中的线程队列。 wake_set_ 竞争中的线程队列。
重载的Wait里面会把线程暂停修改为waiting状态:
调用wait的时候所在线程会加入 wait_set_队列。
接着会执行SignalWaiterAndReleaseMonitorLock函数,这个函数在 wake_set_ 内有线程的时候,监听Signal信号,当监听到Singal的时候,循环结束,走到释放流程。
接着会发出Wait信号:
这个Wait信号对应的真正操作是 pthread_con_wait 信号:
我们查询一下这个函数的文档:
这是一个基于条件变量的阻塞,可以通过pthread_cond_signal来恢复线程。而SignalWaiterAndReleaseMonitorLock里面的Signal就对应的这个调用。
notify/notifyAll
notify
notifyAll
notify和notifyAll调用是类似的,就是把 wait_set_里面的内容移动到 wake_set_里面。这样就对应上了SignalWaiterAndReleaseMonitorLock里面的循环。所以notify和notifyAll只是修改一下队列,阻塞和恢复逻辑都是在wait里面实现的。在wait、notify/notifyAll的调用里面有一个细节,当前线程不持有锁的时候,会抛出“object not locked by thread before wait()”异常:
这里也对应了我们使用对象wait、notify的时候,我们需要在synchronized代码块里面调用。
线程获取锁的时候会执行monitor-ente,释放锁的时候会执行monitor-exit。 对象的结构里通过LockWord维护了锁的状态,锁的状态会决定锁的量级。从无锁状态去获取锁的时候,会把LockWord更新为轻量级锁。轻量级锁用cas方式获取锁,如果超过一定自旋次数没有成功获取轻量级锁,那么锁会升级到重量级锁。 重量级锁去竞争锁的时候会阻塞线程等待获取锁,底层会使用mutex lock实现加锁,这个会涉及jvm线程的阻塞和恢复,所以性能消耗是最大的。
对象调用wait、notify/notifyAll的时候需要再同步代码块里执行
notify/notifyAll只是修改线程队列,阻塞和恢复的逻辑都维护在wait里面。
最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!
推荐阅读:
扫一扫 关注我的公众号
如果你想要跟大家分享你的文章,欢迎投稿~
┏(^0^)┛明天见!