1、AQS是什么

  AQS同步器是Java并发编程的基础,从资源共享的角度分成独占和共享两种模式,像ReentrantLock、ThreadPoolExecutor、CountDownLatch等都是基于AQS来实现的,如图:

2、AQS同步队列的基本结构

  AQS维护了一个头节点(head)和一个尾节点(tail)结构的双向链表,当一个线程获取锁失败时,会将该线程打包成一个Node节点,挂到同步队列尾节点

private transient volatile Node head;//同步队列头结点
private transient volatile Node tail;//同步队列尾结点
private volatile int state;//同步状态 

2.1、同步队列内部结构

2.2、获取同步状态失败,将当前线程和节点模式打包成一个节点,放入同步队列尾部

2.3、前驱节点是头节点并且获取同步状态成功,设置当前节点为头节点,并将前驱节点指针清空,方便GC回收

2.4、Node节点类(双向链表挂在同步器中)

 1 static final class Node {
2
3 static final Node SHARED = new Node();//共享模式
4
5 static final Node EXCLUSIVE = null;//独占模式
6
7 static final int CANCELLED = 1;//线程已取消
8
9 static final int SIGNAL = -1;//后继线程需要取消挂起
10
11 static final int CONDITION = -2;//线程正在等待条件
12
13 static final int PROPAGATE = -3;
14
15 volatile int waitStatus;
16
17 volatile Node prev;//前驱结点
18
19 volatile Node next;//后继结点
20
21 volatile Thread thread;//当前线程
22
23 Node nextWaiter;
24
25 final boolean isShared() {
26 return nextWaiter == SHARED;
27 }
28 //获取前驱节点
29 final Node predecessor() throws NullPointerException {
30 Node p = prev;
31 if (p == null)
32 throw new NullPointerException();
33 else
34 return p;
35 }
36
37 Node() {
38 }
39 //当前线程和节点模式
40 Node(Thread thread, Node mode) {
41 this.nextWaiter = mode;
42 this.thread = thread;
43 }
44
45 Node(Thread thread, int waitStatus) {
46 this.waitStatus = waitStatus;
47 this.thread = thread;
48 }
49 }

Node分析

2.5、state同步状态(AQS重要的成员变量)

 //同步状态
private volatile int state;
//获取同步状态
protected final int getState() {
return state;
}
//设置同步状态
protected final void setState(int newState) {
state = newState;
}
//CAS设置同步状态
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

3、子类需重写的方法

AQS使用了模板方法设计模式,核心框架JDK已经写好,子类(自定义同步器)只需重写如下几个方法,即可实现不同的同步器:

 //用于判断当前方法是否被线程独占,独占锁需重写
protected boolean isHeldExclusively() {
throw new UnsupportedOperationException();
} //独占式获取锁
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
} //独占式释放锁
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
} //共享式获取锁
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
} //共享式释放锁
protected int tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}

 4、独占模式分析

4.1、acquire独占锁获取(ReentrantLock的lock方法就是调用该方法

1、tryAcquire(子类实现的方法,此时派上用场)

尝试获取同步状态,获取成功则直接使用

2、addWaiter

将当前线程打包成一个独占模式节点,放入同步队列的尾部

3、acquireQueued

进入等待状态,直到其他线程唤醒自己

 //获取独占锁入口方法
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
//放入队列
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);//循环CAS方式将节点放入队列尾部
return node;
}
//入队
private Node enq(final Node node) {
for (; ; ) {//循环CAS添加尾部节点
Node t = tail;
if (t == null) { //队列为空,初始化一个空节点
if (compareAndSetHead(new Node()))//CAS防止产生多个队列
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {//CAS设置尾节点
t.next = node;
return t;
}
}
}
}
//阻塞等待
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; //便于GC回收以前的头节点
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&//设置前驱节点状态
parkAndCheckInterrupt())//阻塞线程
interrupted = true;//被中断一次就设置为true
}
} finally {
if (failed)
cancelAcquire(node);
}
}
//设置前驱节点
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;//前驱节点的状态
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {//目的是为了剔除取消的节点
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);//找到一个没有被取消的节点
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);//将前驱节点设置为SIGNAL
}
return false;
}
//阻塞线程
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);//阻塞线程,直到被唤醒(发生中断或被其他线程调用unpark)
return Thread.interrupted();//线程是否中断
}

独占锁获取

4.2、release独占锁释放(ReentrantLock的unlock方法就是调用该方法)

1、tryRelease(子类实现的方法,自定义释放的逻辑)

尝试获取同步状态,成功则继续

2、unparkSuccessor

  找到头节点,唤醒后继线程

 public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;//找到头节点
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);//唤醒后继线程
return true;
}
return false;
}
//唤醒后继线程
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0); Node s = node.next;//获取后继节点
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);//唤醒后继线程
}

独占锁释放

5、共享模式分析

5.1、acquireSharedInterruptibly共享锁获取(Semaphore的acquire方法就是调用该方法)

1、线程是否中断,是则抛出异常

2、tryAcquireShared(子类实现的方法)

尝试获取资源,成功直接返回,失败进入下面流程

3、doAcquireSharedInterruptibly(和独占锁类似)

将当前线程打包成共享节点,放入同步队列并阻塞,直到被唤醒并成功获取到资源才返回

 public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)//子类实现的方法,一般用来判断是否还有资源
doAcquireSharedInterruptibly(arg);//放入同步队列等待
} private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);//将当前线程打包成一个共享节点,放入同步队列尾部
boolean failed = true;
try {
for (;;) {//自旋
final Node p = node.predecessor();//获取前驱节点
if (p == head) {//前驱节点是头节点
int r = tryAcquireShared(arg);//尝试获取资源
if (r >= 0) {//大于0代表有资源可用
setHeadAndPropagate(node, r);//设置自己为head,还有剩余资源则唤醒后继线程
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&//设置前驱节点状态
parkAndCheckInterrupt())//阻塞线程,等待其他线程唤醒或线程被中断
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}

获取共享锁

5.2、releaseShared共享锁释放(Semaphore的release方法就是调用该方法)

 public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
} private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);//唤醒后继线程
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}


待续...


												

AQS分析(AbstractQueuedSynchronizer)(三)的更多相关文章

  1. 【JDK】JDK源码分析-AbstractQueuedSynchronizer(2)

    概述 前文「JDK源码分析-AbstractQueuedSynchronizer(1)」初步分析了 AQS,其中提到了 Node 节点的「独占模式」和「共享模式」,其实 AQS 也主要是围绕对这两种模 ...

  2. 【JDK】JDK源码分析-AbstractQueuedSynchronizer(3)

    概述 前文「JDK源码分析-AbstractQueuedSynchronizer(2)」分析了 AQS 在独占模式下获取资源的流程,本文分析共享模式下的相关操作. 其实二者的操作大部分是类似的,理解了 ...

  3. Java 中队列同步器 AQS(AbstractQueuedSynchronizer)实现原理

    前言 在 Java 中通过锁来控制多个线程对共享资源的访问,使用 Java 编程语言开发的朋友都知道,可以通过 synchronized 关键字来实现锁的功能,它可以隐式的获取锁,也就是说我们使用该关 ...

  4. AQS源码三视-JUC系列

    AQS源码三视-JUC系列 前两篇文章介绍了AQS的核心同步机制,使用CHL同步队列实现线程等待和唤醒,一个int值记录资源量.为上层各式各样的同步器实现画好了模版,像已经介绍到的ReentrantL ...

  5. Linux内核--网络栈实现分析(三)--驱动程序层+链路层(上)

    本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7497260 更多请看专栏,地址 ...

  6. hostapd源代码分析(三):管理帧的收发和处理

    hostapd源代码分析(三):管理帧的收发和处理 原文链接:http://blog.csdn.net/qq_21949217/article/details/46004379 这篇文章我来讲解一下h ...

  7. Linux内核分析(三)----初识linux内存管理子系统

    原文:Linux内核分析(三)----初识linux内存管理子系统 Linux内核分析(三) 昨天我们对内核模块进行了简单的分析,今天为了让我们今后的分析没有太多障碍,我们今天先简单的分析一下linu ...

  8. 一个普通的 Zepto 源码分析(三) - event 模块

    一个普通的 Zepto 源码分析(三) - event 模块 普通的路人,普通地瞧.分析时使用的是目前最新 1.2.0 版本. Zepto 可以由许多模块组成,默认包含的模块有 zepto 核心模块, ...

  9. java AQS(AbstractQueuedSynchronizer)同步器详解

    除了内置锁(synchronized)外,java AQS(AbstractQueuedSynchronizer)同步器几乎是所有同步容器,同步工具类的基础.ReentrantLock.Reentra ...

  10. Koa源码分析(三) -- middleware机制的实现

    Abstract 本系列是关于Koa框架的文章,目前关注版本是Koa v1.主要分为以下几个方面: Koa源码分析(一) -- generator Koa源码分析(二) -- co的实现 Koa源码分 ...

随机推荐

  1. ffplay for mfc 代码备忘

    之前上传了一个开源播放器工程ffplay for mfc.它将ffmpeg项目中的ffplay播放器(ffplay.c)移植到了VC的环境下,并且使用MFC做了一套界面.它可以完成一个播放器播放视频的 ...

  2. SimpleDateFormat用法大全及易错分析

    SimpleDateFormat 使用java语言处理日期相关操作,就不可避免的会使用到SimpleDateFormat.当然了,目前我们采用较多的是Calendar类,通过对之求相关的属性值即可得到 ...

  3. leetcode之 Palindrome Partitioning I&II

    1 Palindrome Partitioning 问题来源:Palindrome Partitioning 该问题简单来说就是给定一个字符串,将字符串分成多个部分,满足每一部分都是回文串,请输出所有 ...

  4. EBS 信用检查(二)

    Credit checking feature of Oracle OM provides the ability to check that the customer has sufficient ...

  5. android 优化之布局优化

    布局优化的思路很简单,尽量减少布局文件的层级,看过系统源码的都知道,Android view绘制都是逐层绘制的,所以布局的层级少了,decodeview的时候绘制工作自然就少了. 那么如何进行布局的优 ...

  6. PO订单审批通过API

    DECLARE l_return_status VARCHAR2(1); l_exception_msg VARCHAR2(4000); BEGIN mo_global.set_policy_cont ...

  7. RAC 10g administration

    10g RAC administration See OCFS Oracle Cluster Filesystem, ASM, TNSnames configuration, Oracle Datab ...

  8. (Struts2)XWork容器的实现机理

    模板方法----callInContext 翻开ContainerImpl的实现,我们可以看到callInContext,这个模板方法是容器所有操作调用的基础. 关于模板方法模式,大家可以看出刘伟老师 ...

  9. 为什么选择Nginx

     Nginx 是一个高性能的 Web 和反向代理服务器, 它具有有很多非常优越的特性: 作为 Web 服务器:相比 Apache,Nginx 使用更少的资源,支持更多的并发连接,体现更高的效率,这 ...

  10. cdh5 hadoop redhat 本地仓库配置

    cdh5 hadoop redhat 本地仓库配置 cdh5 在网站上的站点位置: http://archive-primary.cloudera.com/cdh5/redhat/6/x86_64/c ...