AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)
1. 前言
java中好多地方用到abstractqueuedsynchronizer(ps:简称aqs),比如reentrantlock、线程池,这部分在面试的时候也经常被问到,今天以reentrantlock为例,通过源码来加深对aqs的理解
2. lock
通常,我们的用法是这样的:
那么lock(),unlock()到底做了什么,我们并不知晓,接下来一步一步揭开她的神秘面纱
2.1. lock
可以看到,reentrantlock默认是nonfairsync(非公平锁)
2.2. nonfairsync
可以看到,首先判断同步状态是否是0,如果是0则临界资源没有被占用,并且将状态设为1,当前线程获得这个资源,可以访问;否则,调用acquire(1)方法尝试获取资源的访问控制权。
2.3. tryacquire
尝试再获取一次
如果当前同步状态是0,则跟之前相同,将状态置为1,当前线程获得访问权,返回true
如果当前资源的所有者线程就是当前线程,则状态加1,当前线程仍然获得访问权,返回true
如果以上情况都不是(ps:资源被别的线程占用着),则返回false
2.4. acquirequeued
如果上一步tryacquire方法返回false,则继续调用acquirequeued方法将线程添加到队列中(ps:链表的尾部)
在添加到链表之前,先封装成node对象
将当前线程构造成node对象,节点的模式是排它的,然后将其加到链表的尾部(或是说叫队列),最后将该结点返回
这里有两个特殊的节点:等待队列的头结点 和 等待队列的尾结点
接下来,将刚才构造的节点加到队列中
如果当前节点的前驱节点是头结点(head),并且当前节点再次尝试获取资源(tryacquire方法),恰好成功了,于是皆大欢喜
如果当前节点的前驱节点既不是head,而且当前节点也没有抢占到资源,则循环直到前驱节点的状态变成signal,则挂起当前线程
3. unlock
如果资源的所有者线程不是当前线程的话,则抛出异常
如果当前资源同步状态减1恰好是0,则成功释放,同步状态置为0,资源所有者线程置为null,唤醒head的后继节点
4. 小结
加锁
1、默认非公平锁(ps:当前线程不是直接加到等待队列中,而是先尝试获取一次,如果不成功则加到队列中,在加的时候还会尝试一次)
2、临街资源有一个同步状态,0表示当前没有线程占用,则可以直接可以获取到资源(加锁成功)
3、如果资源所有者线程就是当前线程,则状态加1,仍然可以获得锁
3、加锁成功以后,将同步状态置为1,资源所有者线程置为当前线程
4、不成功,则封装成node加入到等待队列(链表)中,此时,还会像前面一样再尝试(抢占)一次
5、抢占不成功,加到等待队列中,线程挂起
解锁
1、资源所有者线程不是当前线程,则抛异常
2、唤醒下一个结点
推荐阅读
-
ReentrantLock锁 源码分析
-
源码分析Vue.js的监听实现教程
-
并发编程(五)——AbstractQueuedSynchronizer 之 ReentrantLock源码分析
-
[五]类加载机制双亲委派机制 底层代码实现原理 源码分析 java类加载双亲委派机制是如何实现的
-
深入源码分析Spring注解的实现原理---@Import
-
AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)
-
[Abp 源码分析]六、工作单元的实现
-
php实现商城购物车的思路和源码分析
-
netty中的发动机--EventLoop及其实现类NioEventLoop的源码分析
-
源码分析之RocketMQ Producer生产消息的实现及其设计模式分析