【java并发】AQS中acquire方法解析
AQS,全名AbstractQueuedSynchronizer(抽象队列同步器),它是CLH(不明白的可以先了解一下CLH)的变种。它与CLH不同之处在于:
CLH是一种公平锁,它是通过自旋同步队列中节点的前驱结点状态,判断同步队列中的节点是否能够进入临界区;AQS的同步队列中的节点不会以自旋的方式来进入临界区,而是先以公平或者不公平的方式尝试进入临界区,如果不能,则进行阻塞,等待被唤醒,再去尝试是否能够进入临界区。
AQS属于队列,那么就是一个一个节点连接而成,在AQS中节点的数据结构如下:
static final class Node {static final Node SHARED = new Node();/** 独占锁模式 */static final Node EXCLUSIVE = null;/*** AQS中判断节点是否为取消状态,有时候是判断状态值是否大于零*/static final int CANCELLED = 1;/** * 如果节点是这个状态,* 那么该节点就需要唤醒它的后继节点 */static final int SIGNAL = -1;/*** 不明白,但是不影响对acquire方法的理解*/static final int CONDITION = -2;/*** 不明白,但是不影响对acquire方法的理解*/static final int PROPAGATE = -3;/** * 值为:-3 , -2 , -1 , 0 , 1 */volatile int waitStatus;volatile Node prev;volatile Node next;volatile Thread thread;Node nextWaiter;final boolean isShared() {return nextWaiter == SHARED;}final Node predecessor() throws NullPointerException {Node p = prev;if (p == null)throw new NullPointerException();elsereturn p;}Node() {}Node(Thread thread, Node mode) { this.nextWaiter = mode;this.thread = thread;}Node(Thread thread, int waitStatus) {this.waitStatus = waitStatus;this.thread = thread;}}
可以看出来AQS同步队列是一个双向链表结构。需要说明的是除了头节点是进入到临界区的节点,其他大部分节点(有一部分节点可能被取消了,waitStatus=1)都是希望进入临界区的节点。
acquire方法解析
AQS中的acquire方法,是获取独占锁方法,它的代码很简单,代码主体只有一个if判断语句:
public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();
}
可以看到acquire方法实现,主要是下面这三个方法
- tryAcquire(arg)
- addWaiter(Node.EXCLUSIVE)
- acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
这三个方法的作用
- tryAcquire(arg)
顾名思义,它就是尝试获取锁,AQS在这里没有对其进行功能的实现,只有一个抛出异常的语句,用户可以对其重写实现公平锁、不公平锁、可重入锁、不可重入锁
protected boolean tryAcquire(int arg) {throw new UnsupportedOperationException();
}
- addWaiter(Node.EXCLUSIVE)
一旦尝试获取锁未成功,就要使用该方法将其加入同步队列尾部
private Node addWaiter(Node mode) {Node node = new Node(Thread.currentThread(), mode);// 快速尝试加入到同步队列队尾Node pred = tail;if (pred != null) {node.prev = pred;if (compareAndSetTail(pred, node)) {pred.next = node;return node;}}// 如果快速尝试没有成功,则自旋加入队尾,直到成功加入enq(node);return node;
}private Node enq(final Node node) {for (;;) {Node t = tail;if (t == null) { // Must initializeif (compareAndSetHead(new Node()))tail = head;} else {node.prev = t;if (compareAndSetTail(t, node)) {t.next = node;return t;}}}}
由于可能有多个线程并发加入队尾产生竞争,因此,采用compareAndSetTail无锁方法来保证同步
- acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
一旦加入同步队列,就需要使用该方法,自旋阻塞唤醒来不断的尝试获取锁,直到被中断或获取到锁
final boolean acquireQueued(final Node node, int arg) {boolean failed = true;try {boolean interrupted = false;// 自旋for (;;) {final Node p = node.predecessor();if (p == head && tryAcquire(arg)) {setHead(node);p.next = null; // 帮助GCfailed = false;return interrupted;}if (shouldParkAfterFailedAcquire(p, node) /** 尝试获取锁失败是否阻塞线程获取锁 */ &&parkAndCheckInterrupt() /** 阻塞 */)interrupted = true;}} finally {if (failed)cancelAcquire(node);}
}
前面说过,当一个节点的前驱节点的waitStatus=SIGNAL,当其前驱结点释放锁的时候需要对其后继节点进行唤醒。shouldParkAfterFailedAcquire方法的功能就是判断该节点的前驱结点的waitStatus==SIGNAL,如果相等则该节点可以阻塞,否则将该节点的前驱结点waitStatus状态修改为SIGNAL。由此可以知道,该节点如果没有获取到锁,AQS就会尽最大努力(为什么说最大努力,而不是一定会将节点阻塞呢?可以思考一下,为什么,shouldParkAfterFailedAcquire里面能找到答案)将该节点阻塞,之后等待前驱结点唤醒,再尝试获取锁。
【java并发】AQS中acquire方法解析相关推荐
- JUC里面的相关分类|| java并发编程中,关于锁的实现方式有两种synchronized ,Lock || Lock——ReentrantLock||AQS(抽象队列同步器)
JUC分类 java并发编程中,关于锁的实现方式有两种synchronized ,Lock AQS--AbstractQueuedSynchronizer
- 理解Java并发编程:volatile关键字解析
文章目录 volatile关键字作用详解 原子/可见/有序 happen-before原则 volatile的作用 volatile的原理 volatile关键字作用详解 讲到Java中的volati ...
- Java并发编程中的若干核心技术,向高手进阶
来源:http://www.jianshu.com/p/5f499f8212e7 引言 本文试图从一个更高的视角来总结Java语言中的并发编程内容,希望阅读完本文之后,可以收获一些内容,至少应该知道在 ...
- synchronized 异常_由浅入深,Java 并发编程中的 Synchronized
synchronized 作用 synchronized 关键字是 Java 并发编程中线程同步的常用手段之一. 1.1 作用: 确保线程互斥的访问同步代,锁自动释放,多个线程操作同个代码块或函数必须 ...
- Java并发编程:volatile关键字解析(转载)
转自https://www.cnblogs.com/dolphin0520/p/3920373.html Java并发编程:volatile关键字解析 Java并发编程:volatile关键字解析 v ...
- 由浅入深,逐步了解 Java 并发编程中的 Synchronized!
作者 | sowhat1412 责编 | 张文 头图 | CSDN 下载自视觉中国 来源 | sowhat1412(ID:sowhat9094) synchronized 作用 synchroniz ...
- 转载:Java并发编程:volatile关键字解析
看到一篇写的很细致的文章,感谢作者 作者:Matrix海子 出处:http://www.cnblogs.com/dolphin0520/ 本博客中未标明转载的文章归作者Matrix海子和博客园共有,欢 ...
- java 线程由浅入深_由浅入深,Java 并发编程中的 Synchronized(一)
synchronized 作用 synchronized 关键字是 Java 并发编程中线程同步的常用手段之一. 1.1 作用: 确保线程互斥的访问同步代,锁自动释放,多个线程操作同个代码块或函数必须 ...
- java empty isempty_Java中isEmpty方法如何使用?
展开全部 isEmpty()方法有很多类都有,对于String类,62616964757a686964616fe4b893e5b19e31333264656134它是Java 6.0引入的, 当且仅当 ...
最新文章
- angular2集成highchart
- react-dnd 拖拽
- 走近webpack(3)--图片的处理
- 数据分析实战-PUBG数据集EDA
- Qt的Assistant制作自定义的软件帮助界面(记录)
- [导入]2007年美国电影上映时间表
- C++_虚函数的实现的基本原理
- jQueryWEUI自定义对话框-带有textarea
- 两个小球碰撞速度计算方法
- 每年都要调两次时间,美国人已经烦透了
- Pidgin 2.3.1语言设置方法
- [9i]多练扎马步,预防膝盖疼痛
- Jinja2安装与基本API用法
- Win11如何自动清理垃圾?Win11自动删除文件设置方法
- 医疗器械软件网络安全相关
- linux mock 使用
- SHT10型温湿度传感器工作时序分析及驱动程序与Proteus仿真的实现
- 实话实说 —— 心理模型vs实现模型
- linux安装宝塔面板命令大全
- 80GB医学影像数据集发布!OCTA-500公开下载
热门文章
- 用计算机弹追光使者,【B型】追光使者-洛天依(完整版歌词附)
- 钢铁产能年过剩超亿吨 发改委铁腕治“劣”
- VanillaNet:极简主义在深度学习中的力量
- 《炬丰科技-半导体工艺》 迈向硅衬底上的紫外光电系统
- 滴滴出行与上海交通大学共建联合实验室,加速产学研合作进程
- python数据挖掘商业案例_Python数据科学-技术详解与商业实践-第八讲作业
- 艾媒报告|2018-2019中国智慧物流行业研究报告
- 常州工学院c语言试题 选择题,常州工学院高等数学(上)综合测试题20.doc
- 阿里云超算战纪 | 凌云时刻
- 一种日志采集装置及方法