欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)

程序员文章站 2023-01-03 17:52:07
1. 前言 Java中好多地方用到AbstractQueuedSynchronizer(PS:简称AQS),比如ReentrantLock、线程池,这部分在面试的时候也经常被问到,今天以ReentrantLock为例,通过源码来加深对AQS的理解 2. lock 通常,我们的用法是这样的: 那么lo ......

1.  前言

java中好多地方用到abstractqueuedsynchronizer(ps:简称aqs),比如reentrantlock、线程池,这部分在面试的时候也经常被问到,今天以reentrantlock为例,通过源码来加深对aqs的理解

AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)

2.  lock

通常,我们的用法是这样的:

AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)

那么lock(),unlock()到底做了什么,我们并不知晓,接下来一步一步揭开她的神秘面纱

2.1.  lock

AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)

可以看到,reentrantlock默认是nonfairsync(非公平锁)

2.2.  nonfairsync

AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)

AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)

AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)

可以看到,首先判断同步状态是否是0,如果是0则临界资源没有被占用,并且将状态设为1,当前线程获得这个资源,可以访问;否则,调用acquire(1)方法尝试获取资源的访问控制权。

2.3.  tryacquire

AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)

AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)

AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)

 

尝试再获取一次

如果当前同步状态是0,则跟之前相同,将状态置为1,当前线程获得访问权,返回true

如果当前资源的所有者线程就是当前线程,则状态加1,当前线程仍然获得访问权,返回true

如果以上情况都不是(ps:资源被别的线程占用着),则返回false

2.4.  acquirequeued

如果上一步tryacquire方法返回false,则继续调用acquirequeued方法将线程添加到队列中(ps:链表的尾部)

在添加到链表之前,先封装成node对象

AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)

AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)

AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)

将当前线程构造成node对象,节点的模式是排它的,然后将其加到链表的尾部(或是说叫队列),最后将该结点返回

这里有两个特殊的节点:等待队列的头结点 和 等待队列的尾结点

AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)

接下来,将刚才构造的节点加到队列中

AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)

AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)

如果当前节点的前驱节点是头结点(head),并且当前节点再次尝试获取资源(tryacquire方法),恰好成功了,于是皆大欢喜

如果当前节点的前驱节点既不是head,而且当前节点也没有抢占到资源,则循环直到前驱节点的状态变成signal,则挂起当前线程

AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)

3.  unlock

AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)

AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)

AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)

如果资源的所有者线程不是当前线程的话,则抛出异常

如果当前资源同步状态减1恰好是0,则成功释放,同步状态置为0,资源所有者线程置为null,唤醒head的后继节点

AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)

4.  小结

加锁

1、默认非公平锁(ps:当前线程不是直接加到等待队列中,而是先尝试获取一次,如果不成功则加到队列中,在加的时候还会尝试一次)

2、临街资源有一个同步状态,0表示当前没有线程占用,则可以直接可以获取到资源(加锁成功)

3、如果资源所有者线程就是当前线程,则状态加1,仍然可以获得锁

3、加锁成功以后,将同步状态置为1,资源所有者线程置为当前线程

4、不成功,则封装成node加入到等待队列(链表)中,此时,还会像前面一样再尝试(抢占)一次

5、抢占不成功,加到等待队列中,线程挂起

解锁

1、资源所有者线程不是当前线程,则抛异常

2、唤醒下一个结点