ReentrantLock源码分析(下)

编程入门 行业动态 更新时间:2024-10-18 08:35:50

ReentrantLock<a href=https://www.elefans.com/category/jswz/34/1770099.html style=源码分析(下)"/>

ReentrantLock源码分析(下)

序 -- 早年间由于synchronized关键字的效率问题,导致在jdk1.6版本之前,著名的daog lee先生编写出了ReentrantLock提供给我们使用,一方面是因为synchronized锁在jdk1.6之间是一把重量级锁,无论是线程的交替执行,或者是并发执行,都会调用OS系统的函数,导致及其耗费时间,ReentrantLock却在java的层面上大幅度的解决了这一问题,今天,我们就来研究一下ReentrantLock的源码,看看它是如何做到的性能优化,
 

正文 -- ReentrantLock的公平锁(解锁机制)的实现

承接上篇,我们已经说完了ReentrantLock的公平锁是如何将对象锁定,今天我们来聊一聊如何将锁定的对象进行解锁,并唤醒正在排队的对象. 如果对锁定机制还有不熟悉的同学请去ReentrantLock源码解析(上)查看.

首先熟悉的流程,我们先进入到ReentrantLock锁的unlock方法:

public void unlock() {this.sync.release(1);
}//与lock方法非常相似,首先先进行tryRelease方法进行询问,询问是否需要唤醒正在等待的线程.
public final boolean release(int arg) {if (this.tryRelease(arg)) {//获取队头,即Thread为null的那个节点AbstractQueuedSynchronizer.Node h = this.head;if (h != null && h.waitStatus != 0) {//将队头节点传入,开始唤醒位于队头之后的第一个节点,即排队时间最长的节点.this.unparkSuccessor(h);}return true;} else {return false;}
}

看完解锁流程,我们会发现在解锁流程中最重要的只有两个方法,首先我们来看第一个tryRelease方法:

protected final boolean tryRelease(int releases) {//首先获取ReentrantLock对象中锁的状态,State在前文中说过是重入锁机制.//当正在进行的线程没有被重入时,c的值应该为0//当正在进行的线程被重入过时,state的值会递减1,代表重入已经完成一次,以此同时,等待的线程不会被唤醒,方法返回.int c = this.getState() - releases;if (Thread.currentThread() != this.getExclusiveOwnerThread()) {throw new IllegalMonitorStateException();} else {boolean free = false;if (c == 0) {//当前线程没有被重入,且已经完成时,将ReentrantLock对象当前线程置为空free = true;this.setExclusiveOwnerThread((Thread)null);}//当前线程没有被重入,将锁的状态码置为0,并返回this.setState(c);return free;}}

紧接着看unparkSuccessor方法,也就是解锁过程中最后的一步:

//首先我们传入的队头节点,即Thread为null的节点
private void unparkSuccessor(AbstractQueuedSynchronizer.Node node) {//获取队头节点的waitStatus状态码,由于将第一个等待线程入队时,就已经将队头节点的状态码修改为-1.//此时我们将直接进行if判断,并进行CAS操作,将队头的状态码置为0.int ws = node.waitStatus;if (ws < 0) {node.compareAndSetWaitStatus(ws, 0);}//接着获取到队头的下一个节点,即排队最靠前的节点//此时的s不为null,所以我们将s所持有的线程唤醒.AbstractQueuedSynchronizer.Node s = node.next;if (s == null || s.waitStatus > 0) {s = null;for(AbstractQueuedSynchronizer.Node p = this.tail; p != node && p != null; p = p.prev) {if (p.waitStatus <= 0) {s = p;}}}if (s != null) {LockSupport.unpark(s.thread);}}

在唤醒了正在排队的第一个线程以后,别忘记了这个线程处于的位置,此时的线程正处于parkAndCheckInterrupt方法中.

final boolean acquireQueued(AbstractQueuedSynchronizer.Node node, int arg) {//判断是否需要打断该线程的标机,暂时不需要管.boolean interrupted = false;try {while(true) {//第二次循环,同样获取队头的Node节点AbstractQueuedSynchronizer.Node p = node.predecessor();//由于当前正在进行的线程已经将锁释放,此时我们能拿到锁,并将队头的null的pre与需要获取锁的节点的next断开,以便GC能够进行回收.-->最终,方法返回,线程开始执行任务if (p == this.head && this.tryAcquire(arg)) {this.setHead(node);p.next = null;return interrupted;}---->当线程被唤醒时处于这里,并且直接进入下一次循环.if (shouldParkAfterFailedAcquire(p, node)) {interrupted |= this.parkAndCheckInterrupt();}}} catch (Throwable var5) {this.cancelAcquire(node);if (interrupted) {selfInterrupt();}throw var5;}}

至此,ReentrantLock公平锁的锁定与唤醒机制我们已经全部讲完.

如果大家有疑问的可以直接私信问我,我会一一解答.

更多推荐

ReentrantLock源码分析(下)

本文发布于:2023-07-28 17:54:53,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1268426.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:源码   ReentrantLock

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!