4.JUC之AQS框架
一、简介
1.AQS
AQS是AbstractQueuedSynchronizer的简写,直白的翻译:抽象队列同步器,jdk1.5后出现
Provides a framework for implementing blocking locks and related synchronizers (semaphores, events, etc) that rely on first-in-first-out (FIFO) wait queues. This class is designed to be a useful basis for most kinds of synchronizers that rely on a single atomic
int
value to represent state. Subclasses must define the protected methods that change this state, and which define what that state means in terms of this object beingacquired
orreleased
. Given these, the other methods in this class carry out all queuing and blocking mechanics. Subclasses can maintain other state fields, but only the atomically updated int value manipulated using methods getState, setState and compareAndSetState is tracked with respect to synchronization. ——Java docs
翻开源码,首先是一大段的注释,然后可以看开AQS继承自AbstractOwnableSynchronizer。当你翻开AbstractOwnableSynchronizer时,还是先看到小段的说注释。
A synchronizer that may be exclusively owned by a thread. This class provides a basis for creating locks and related synchronizers that may entail a notion of ownership. The AbstractOwnableSynchronizer class itself does not manage or use this information. However, subclasses and tools may use appropriately maintained values to help control and monitor access and provide diagnostics. ——Java docs
2.AOS
生自java1.6,可以由线程以独占的方式拥有的同步器。此类为创建锁和相关同步器(伴随着所有权的概念)提供了基础。
AbstractOwnableSynchronizer类本身不管理或使用此信息。但是,子类和工具可以使用适当维护的值帮助控制和监视访问以及提供诊断
简而言之,AOS输出的是一种概念,是一种创建独占锁同步器所有权的概念。他自身并没有规范所有权的管理方式,更没有用到这些信息(所有权的信息)。不过呢,可以帮其子类维护所有者信息。
可能上面的话仍然不够清晰,下面看看源码。
AOS提供两个方法一个字段,外带一个空构造方法(可无视)
/**
* The current owner of exclusive mode synchronization.
*/
private transient Thread exclusiveOwnerThread; // transient表示该字段不需要序列化,因为AOS实现了Serializable protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
} protected final Thread getExclusiveOwnerThread() {
return exclusiveOwnerThread;
}
这下明白了吧,就是AOS只维护一个东西------当前谁持有独占式同步器。因此咱们可以当他不存在,继续回到AQS
二、作用
从Java Docs的注释可以知道,AQS是一个框架,一个提供锁或同步器依赖于FIFO等待队列所必要的 “基础设施”的框架,Doug Lea之所以写个抽象类的目的是为了简化我们实现同步器的工作
也就是说:
提供一个基于FIFO等待队列,可以用于构建锁或其他同步装置的基础框架。意在能够实现大部分同步需求的基础。
AQS默认提供了独占式和共享式两种模式,JDK对应的实现有ReentrantLock 和 ReentrantReadWriteLock。即除了提供acquire方法之外,还提供了acquireShare。带shared都是共享模式相关操作,默认则是独占模式。
AQS到底提供了哪些便利呢:
1.管理状态
2.实现了线程按安全的CLH队列
3.实现了同步器公共方法
4.ConditionObject
三、原理
AQS自身就是一个Wait Queue,CLH lock queue的变种,CLH队列通常用于自旋锁。
The wait queue is a variant of a “CLH” (Craig, Landin, and Hagersten) lock queue. CLH locks are normally used for spinlocks. We instead use them for blocking synchronizers, but use the same basic tactic of holding some of the control information about a thread in the predecessor of its node.
我们知道Queue的底层实现是一个链表结构,我们也知道它可以是单链表,也可以是双链表。AQS实现的是一个双链表结构。即每个节点都存储了前驱和后继两个指针,而且Head节点是dummy节点。入队则tail前驱一个节点,再把新节点接入,然后接入tail节点。
AQS完全通过CAS解决多线程安全问题,即通过Unsafe完成的。具体可以自行阅读AQS源码,在最后几个方法,非常简单
CAS:compareAndSet,Unsafe的CAS方法的语义是先与期望值进行比较,若不相等返回false;若相等的话,则会把原值修改为新值,再返回true。这个跟 java.util.concurrent.atomic的原子类的CAS方法是一样的
AQS的实现依赖LockSupport完成阻塞和唤醒,也正是LockSupport的引入使用AQS源码阅读起来逻辑上有点跳跃。
什么是自旋锁,如下实例实现的即是自旋锁,即是循环等待信息量变成期望值后完成相关操作
// 来自 AQS#doAcquireInterruptibly
private void doAcquireInterruptibly(int arg) throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) { // 自旋锁
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) { // 相当于阻塞到信号量变成期望值
setHead(node);
p.next = null; // help GC
failed = false;
return;
}
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
注:CAS算法本身不会自旋,但是可以自旋CAS来不停地发送请求,如java.util.concurrent.atomic包,这个包里面提供了一组原子类
我们来看一段AtomicBoolean中的自旋锁的代码
public final boolean getAndSet(boolean newValue) {
for (;;) {
boolean current = get();
if (compareAndSet(current, newValue))
return current;
}
}
四、用法
AQS的用法,可以直接参考AQS的注释,另外也可以参考ReentrantLock的写法
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
注释指明了,继承AQS需要实现下面五个方法,
tryAcquire(int arg)
tryRelease(int arg)
tryAcquireShared(int arg)
tryReleaseShared(int arg)
isHeldExclusively()
它们带的int参数,用来表示状态,如下面的示例中,用了1和0表示锁和释放
如你所知,前两个是独占式,中间两个是共享式。最后一个是测试同步器是否正被持有独占锁。即是已有线程持有这个排它锁,且还未释放
后面一篇博客分析这些方法在什么情况下被调用,是怎么和我们的程序联系在一起的
class Mutex implements Lock, java.io.Serializable { // Our internal helper class
private static class Sync extends AbstractQueuedSynchronizer {
// Report whether in locked state
protected boolean isHeldExclusively() {
return getState() == 1;
} // Acquire the lock if state is zero
public boolean tryAcquire(int acquires) {
assert acquires == 1; // Otherwise unused
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
} // Release the lock by setting state to zero
protected boolean tryRelease(int releases) {
assert releases == 1; // Otherwise unused
if (getState() == 0)
throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0);
return true;
} // Provide a Condition
Condition newCondition() {
return new ConditionObject();
} // Deserialize properly
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
} // The sync object does all the hard work. We just forward to it.
private final Sync sync = new Sync(); public void lock() {
sync.acquire(1);
} public boolean tryLock() {
return sync.tryAcquire(1);
} public void unlock() {
sync.release(1);
} public Condition newCondition() {
return sync.newCondition();
} public boolean isLocked() {
return sync.isHeldExclusively();
} public boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
} public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
} public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
}
原文链接:http://blog.csdn.net/zteny/article/details/54919765
4.JUC之AQS框架的更多相关文章
- 《The java.util.concurrent Synchronizer Framework》 JUC同步器框架(AQS框架)原文翻译
一.论文简介 闲来无事,看看源码,发现了一篇JDK作者的论文<The java.util.concurrent Synchronizer Framework>主要描述了作者对Abstrac ...
- Java并发框架——什么是AQS框架
什么是AQS框架 1995年sun公司发布了第一个java语言版本,可以说从jdk1.1到jdk1.4期间java的使用主要是在移动应用和中小型企业应用中,在此类领域中基本不用设计大型并发场景,当然也 ...
- java并发包分析之———AQS框架
一.什么是同步器 多线程并发的执行,之间通过某种 共享 状态来同步,只有当状态满足 xxxx 条件,才能触发线程执行 xxxx . 这个共同的语义可以称之为同步器.可以认为以上所有的锁机制都可以基 ...
- AbstractQueuedSynchronizer AQS框架源码剖析
一.引子 Java.util.concurrent包都是Doug Lea写的,来混个眼熟 是的,就是他,提出了JSR166(Java Specification RequestsJava 规范提案), ...
- 【JUC】JUC线程池框架综述
一.前言 在分析完了JUC的锁和集合框架后,下面进入JUC线程池框架的分析,下面给出JUC线程池的总体框架,之后再逐一进行分析. 二.JUC线程池框架图 说明:从上图可知,JUC线程池框架中的其他接口 ...
- Java并发包源码学习之AQS框架(四)AbstractQueuedSynchronizer源码分析
经过前面几篇文章的铺垫,今天我们终于要看看AQS的庐山真面目了,建议第一次看AbstractQueuedSynchronizer 类源码的朋友可以先看下我前面几篇文章: <Java并发包源码学习 ...
- Java并发包源码学习之AQS框架(三)LockSupport和interrupt
接着上一篇文章今天我们来介绍下LockSupport和Java中线程的中断(interrupt). 其实除了LockSupport,Java之初就有Object对象的wait和notify方法可以实现 ...
- Java并发包源码学习之AQS框架(二)CLH lock queue和自旋锁
上一篇文章提到AQS是基于CLH lock queue,那么什么是CLH lock queue,说复杂很复杂说简单也简单, 所谓大道至简: CLH lock queue其实就是一个FIFO的队列,队列 ...
- Java并发包源码学习之AQS框架(一)概述
AQS其实就是java.util.concurrent.locks.AbstractQueuedSynchronizer这个类. 阅读Java的并发包源码你会发现这个类是整个java.util.con ...
随机推荐
- Slf4j与log4j及log4j2、logbak的关系及使用方法
Slf4j与log4j及log4j2的关系及使用方法 slf4j slf4j仅仅是一个为Java程序提供日志输出的统一接口,并不是一个具体的日志实现方案,就比如JDBC一样,只是一种规则而已,所以单独 ...
- 【opencv】opencv函数isContinuous
isContinuous 参考 1. opencv_isContinuous; 完
- 【Leetcode_easy】1046. Last Stone Weight
problem 1046. Last Stone Weight 参考 1. Leetcode_easy_1046. Last Stone Weight; 完
- vue-cli3使用vue-router 使用动态路由,在刷新页面时报错
刚发现的一个问题,在使用vue-cli3创建项目之后,使用动=动态路由,demo: { path: '/aa/:id', name: 'aa', component: aa }, 编程式路由: thi ...
- 基于 Egg.js 框架的 Node.js 服务构建之用户管理设计
前言 近来公司需要构建一套 EMM(Enterprise Mobility Management)的管理平台,就这种面向企业的应用管理本身需要考虑的需求是十分复杂的,技术层面管理端和服务端构建是架构核 ...
- 【ARTS】01_42_左耳听风-201900826~201900901
ARTS: Algrothm: leetcode算法题目 Review: 阅读并且点评一篇英文技术文章 Tip/Techni: 学习一个技术技巧 Share: 分享一篇有观点和思考的技术文章 Algo ...
- PMP备考笔记--1.1
题型 200道中英文单选题 基本概念题(%10) 过程工具/技术和输入输出题ITTO (%10) 情景题(%70) 计算题(3-5道题) 图 pmp四大挑战 试卷100页,题干长,阅读量大,考试4个小 ...
- __init__调用之二
class Bar: def __init__(self,name,age): self.suibian = name #self后的名字是啥,对象就可以调用啥,而不是 __init__ 后括号形参 ...
- JVM(五) 生产环境内存溢出调优
1.gc配置参数 1.1 控制台打印gc日志 -verbose:gc -XX:+PrintGCDetails -XX:+PrintHeapAtGC(详细的gc信息) 1.2 输出gc日志到指定文件 - ...
- 在Ubuntu下安装qt
安装qt前的准备: 安装基础开发包,主要gcc,g++等: sudo apt-get install build-essential 1. 使用qt-opensource-linux-x86-5.5. ...