首页 > 代码库 > java并发-ReentrantLock源码分析
java并发-ReentrantLock源码分析
1关于可重入锁
ReentrantLock是基于AQS实现的可重入的同步工具类,它提供了两种同步器的实现即公平锁FairSync和非公平锁NonfairSync。它提供了一种无条件的、可轮询的、定时的以及可中断的锁获取操作,所有的加锁和解锁都是显式的。tryLock(),tryLock(long ,TimeUnit)分别提供了可轮询的、可定时的锁获取方式。 Lock()提供了无条件地轮询获取锁的方式。lockInterruptibly()提供了可中断的锁获取方式。 ReentrantLock它提供了两种锁机制,默认创建的是非公平锁。AQS的类结构图如下:
在公平锁上,线程是按照他们发出请求的顺序,如果锁被某个线程占用或者AQS队列中有等待线程,则该请求线程将进入队列排队直到获取锁。源码:
final void lock() { acquire(1);}
非公平锁在请求获取锁时,直接尝试CAS操作获取锁,如果尝试失败才进行排队。在竞争激烈的情况下,非公平锁的性能高于公平锁的一个原因是:活动线程直接尝试获取锁的操作时间可能要比恢复一个阻塞线,并把锁分配给它的时间短的多(线程的唤醒操作需要额外的时间,从唤起到线程真正运行之间存在着严重的时延)。
final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }<span style="font-family:Calibri;"> </span>
2 NonfairSync锁
非公平锁的获取流程,允许插队,直接尝试CAS操作。
3 acquire(1)流程
acquire的获取流程是:先尝试获取锁,如果失败,则将当前节点从队尾添加进入AQS队列等待。操作返回值为空。如果线程在等待过程时中断标识为真,则中断当前线程。
4 tryAcquire(1)流程
tryAcquire(1)操作返回布尔类型,标识是否成功获取锁。如果空闲时CAS失败返回false,如果是当前线程持有锁,则说明是线程重入操作,将锁的重入次数累加,返回true.否则获取操作失败,返回false.
5 addWaiter入队流程
addWaiter将当前线程封装成一个队列节点Node,以for循环的方式重复尝试将节点插入AQS等待队列,直到操作成功返回该Node。AQS维持了一个链表,head和tail两个属性,初始时均为空。在添加第一个节点时会先创建一个虚拟的头节点(即new Node()没有任何信息的节点),并将tail指向head。新节点从队尾以CAS原子操作插入,插入操作在for循环中,以保证线程无法获取锁的时候一定会被添加到等待队列中。
6 acquireQueued排队线程获取锁流程
获取失败的锁将会不断重试,直到线程被阻塞或者获取到锁为止。操作返回值是线程中断标识,如果在阻塞过程中被中断,则返回true给acquire操作,由其中断自己。为什么会有这种判断呢?我的理解:是为了保证线程阻塞过程中,中断信号不被淹没。如果在该线程被park后,其他线程向其发送了中断信号,则正常来说它是不能响应该请求的。这就是parkAndCheckInterrupt操作的必要,它在线程唤起时检查中断标志并通知该线程,由其去中断自己。(即acquire的selefIntrupt()操作)。
7 shouldParkAfterFailedAcquire
某个节点尝试获取锁操作,失败后,根据前驱节点的状态判断是否需要挂起该线程,该方法返回布尔类型值,标识当前节点是否需要被挂起。具体流程如下:
至此,锁获取操作流程介绍。ReentrantLock的lock操作的结果是,要么线程被挂起,要么循环轮询获取锁直到成功设置状态为1(占用状态),然后被移除排队队列。
8 unlock操作分析
锁释放操作只有当前线程是锁的持有者时才能进行,否则会抛出IllegalMonitorStateException。尝试释放锁,如果是成功且等待队列非空,则唤醒它的后继节点。唤醒后继节点的过程中,只有当其后继节点非空且非取消时,调用LockSupport的unpark唤醒,否则一直循环查找直到找到一个可唤起的后继节点为止。
java并发-ReentrantLock源码分析