概念

AQS全称 AbstractQueuedSynchronizer。

AQS是一个并发包的基础组件,用来实现各种锁,各种同步组件的。它包含了state变量、加锁线程、等待队列等并发中的核心组件。

ReentrantLock、Semaphore、CountDownLatch、FutrueTask,这些都是基于AQS构建的。

而AQS是基于volatile变量的读/写和CAS( 也就是compareAndSet()方法 )实现的。

volatile可以保证并发中的可见性,还可以禁止指令重排序。CAS,用于管理对共享数据的并发访问。

ReentrantLock和AQS的关系

在ReentrantLock中,state代表了加锁的状态。初始状态下,这个state的值是0。

另外,这个AQS内部还有一个关键变量,用来记录当前加锁的是哪个线程,初始化状态下,这个变量是null。

线程A跑过来调用ReentrantLock的lock()方法尝试进行加锁,这个加锁的过程,直接就是用CAS操作将state值从0变为1。

如果之前没人加过锁,那么state的值肯定是0,此时线程A就可以加锁成功。

一旦线程1加锁成功了之后,就可以设置当前加锁线程是自己。

主要流程

重要的变量

  • state

    状态变量。
/**
* The synchronization state.
*/
private volatile int state;

等待队列的节点Node

"CLH" lock queue,代表“等待锁的线程组成的队列”,以下简称线程队列。

线程队列,通常被用来处理并发的情况,它通过双向队列(FIFO)来完成同步状态。

每个线程都会被封装成一个Node节点放到同步队列中。

队列的每个Node节点保存了当前线程的同步状态,等待状态,上一个节点和下一个节点等。

static final class Node {
//共享模式
/** Marker to indicate a node is waiting in shared mode */
static final Node SHARED = new Node();
//独占模式
/** Marker to indicate a node is waiting in exclusive mode */
static final Node EXCLUSIVE = null; // 线程的等待状态 表示线程已经被取消
static final int CANCELLED = 1;
// 线程的等待状态 表示后继线程需要被唤醒
static final int SIGNAL = -1;
// 线程的等待状态 表示线程在Condtion上
static final int CONDITION = -2;
// 表示下一个acquireShared需要无条件的传播
static final int PROPAGATE = -3; /**
* 等待状态有以下几种:
*
* SIGNAL: 当前节点的后继节点处于等待状态时,如果当前节点的同步状态被释放或者取消,
* 必须唤起它的后继节点
*
* CANCELLED: 一个节点由于超时或者中断需要在CLH队列中取消等待状态,被取消的节点不会再次等待
*
* CONDITION: 当前节点在等待队列中,只有当节点的状态设为0的时候该节点才会被转移到同步队列
*
* PROPAGATE: 下一次的共享模式同步状态的获取将会无条件的传播 * waitStatus的初始值时0,使用CAS来修改节点的状态
*/
volatile int waitStatus; /**
* Link to predecessor node that current node/thread relies on
* for checking waitStatus. Assigned during enqueuing, and nulled
* out (for sake of GC) only upon dequeuing. Also, upon
* cancellation of a predecessor, we short-circuit while
* finding a non-cancelled one, which will always exist
* because the head node is never cancelled: A node becomes
* head only as a result of successful acquire. A
* cancelled thread never succeeds in acquiring, and a thread only
* cancels itself, not any other node.
*/
volatile Node prev; /**
* Link to the successor node that the current node/thread
* unparks upon release. Assigned during enqueuing, adjusted
* when bypassing cancelled predecessors, and nulled out (for
* sake of GC) when dequeued. The enq operation does not
* assign next field of a predecessor until after attachment,
* so seeing a null next field does not necessarily mean that
* node is at end of queue. However, if a next field appears
* to be null, we can scan prev's from the tail to
* double-check. The next field of cancelled nodes is set to
* point to the node itself instead of null, to make life
* easier for isOnSyncQueue.
*/
volatile Node next; /**
* The thread that enqueued this node. Initialized on
* construction and nulled out after use.
*/
volatile Thread thread; /**
* Link to next node waiting on condition, or the special
* value SHARED. Because condition queues are accessed only
* when holding in exclusive mode, we just need a simple
* linked queue to hold nodes while they are waiting on
* conditions. They are then transferred to the queue to
* re-acquire. And because conditions can only be exclusive,
* we save a field by using special value to indicate shared
* mode.
*/
Node nextWaiter; /**
* Returns true if node is waiting in shared mode.
*/
final boolean isShared() {
return nextWaiter == SHARED;
} /**
* Returns previous node, or throws NullPointerException if null.
* Use when predecessor cannot be null. The null check could
* be elided, but is present to help the VM.
*
* @return the predecessor of this node
*/
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
} Node() { // Used to establish initial head or SHARED marker
} Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
} Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}

acquire()

线程在独占模式下获取state。

子类继承AQS后,经常会用到acquire() 、 release()。

acquire()获取状态、维护状态。

/**
* Acquires in exclusive mode, ignoring interrupts. Implemented
* by invoking at least once {@link #tryAcquire},
* returning on success. Otherwise the thread is queued, possibly
* repeatedly blocking and unblocking, invoking {@link
* #tryAcquire} until success. This method can be used
* to implement method {@link Lock#lock}.
*
* @param arg the acquire argument. This value is conveyed to
* {@link #tryAcquire} but is otherwise uninterpreted and
* can represent anything you like.
*/
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
//通过interrupt() 方法改变中断状态
selfInterrupt();
}

release()

线程在独占模式下释放state。

/**
* Releases in exclusive mode. Implemented by unblocking one or
* more threads if {@link #tryRelease} returns true.
* This method can be used to implement method {@link Lock#unlock}.
*
* @param arg the release argument. This value is conveyed to
* {@link #tryRelease} but is otherwise uninterpreted and
* can represent anything you like.
* @return the value returned from {@link #tryRelease}
*/
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}

unparkSuccessor()

唤醒Node的后继节点。

    /**
* Wakes up node's successor, if one exists.
*
* @param node the node
*/
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;
//通过CAS改变等待状态。
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0); /*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
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);
}

compareAndSetState()

通过CAS设置状态变量。当state值为指定的参数expect时,将其修改为另一个指定的值。

/**
* Atomically sets synchronization state to the given updated
* value if the current state value equals the expected value.
* This operation has memory semantics of a {@code volatile} read
* and write.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful. False return indicates that the actual
* value was not equal to the expected value.
*/
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

ConditionObject

而另一个内部类ConditionObject实现了Condition接口,并且实现了其中的await(),signal(),signalALL()等方法。

ConditionObject主要是为并发编程中的同步提供了等待通知的实现方式,可以在不满足某个条件的时候挂起线程等待。直到满足某个条件的时候在唤醒线程。

参考资料:

https://juejin.im/post/5c07e59cf265da617464a09c (大白话聊聊对AQS的理解)

http://ifeve.com/introduce-abstractqueuedsynchronizer/

https://blog.csdn.net/qq_30572275/article/details/80297047

AQS源码的简单理解的更多相关文章

  1. 硬核剖析Java锁底层AQS源码,深入理解底层架构设计

    我们常见的并发锁ReentrantLock.CountDownLatch.Semaphore.CyclicBarrier都是基于AQS实现的,所以说不懂AQS实现原理的,就不能说了解Java锁. 上篇 ...

  2. AQS源码阅读笔记(一)

    AQS源码阅读笔记 先看下这个类张非常重要的一个静态内部类Node.如下: static final class Node { //表示当前节点以共享模式等待锁 static final Node S ...

  3. 从源码角度彻底理解ReentrantLock(重入锁)

    目录 1.前言 2.AbstractQueuedSynchronizer介绍 2.1 AQS是构建同步组件的基础 2.2 AQS的内部结构(ReentrantLock的语境下) 3 非公平模式加锁流程 ...

  4. 并发-AQS源码分析

    AQS源码分析 参考: http://www.cnblogs.com/waterystone/p/4920797.html https://blog.csdn.net/fjse51/article/d ...

  5. AQS源码分析笔记

    经过昨晚的培训.对AQS源码的理解有所加强,现在写个小笔记记录一下 同样,还是先写个测试代码,debug走一遍流程, 然后再总结一番即可. 测试代码 import java.util.concurre ...

  6. java并发系列(四)-----源码角度彻底理解ReentrantLock(重入锁)

    1.前言 ReentrantLock可以有公平锁和非公平锁的不同实现,只要在构造它的时候传入不同的布尔值,继续跟进下源码我们就能发现,关键在于实例化内部变量sync的方式不同,如下所示: /** * ...

  7. JUC并发编程基石AQS源码之结构篇

    前言 AQS(AbstractQueuedSynchronizer)算是JUC包中最重要的一个类了,如果你想了解JUC提供的并发编程工具类的代码逻辑,这个类绝对是你绕不过的.我相信如果你是第一次看AQ ...

  8. AQS源码深入分析之共享模式-你知道为什么AQS中要有PROPAGATE这个状态吗?

    本文基于JDK-8u261源码分析 本篇文章为AQS系列文的第二篇,前文请看:[传送门] 第一篇:AQS源码深入分析之独占模式-ReentrantLock锁特性详解 1 Semaphore概览 共享模 ...

  9. AQS源码一窥-JUC系列

    AQS源码一窥 考虑到AQS的代码量较大,涉及信息量也较多,计划是先使用较常用的ReentrantLock使用代码对AQS源码进行一个分析,一窥内部实现,然后再全面分析完AQS,最后把以它为基础的同步 ...

随机推荐

  1. Java面试-TCP连接及其优化

    作为一个后端程序员,网络连接这块是一个绕不过的砍,当你在做服务器优化的时候,网络优化也是其中一环,那么作为网络连接中最基础的部分-TCP连接你了解吗?今天我们来仔细看看这个部分. TCP建立连接-三次 ...

  2. Redis(二)特性和学习路线

    Redis是高效的内存数据库或者说缓存.对多种数据结构都支持,每种数据结构都有相应的应用场景. 特性 Redis支持非常多的特性,从用途.性能.高可用.数据安全方面都提供了相应的支持和保障. Redi ...

  3. js、jscore与webkit、nodejs的关系

    js:编程语言 jscore:js语言的解释执行引擎,代表cpu: webkit.nodejs:js语言的运行平台,代表操作系统.计算机资源.

  4. VS2019无法安装Android SDK 28的问题

    在一台新电脑上安装VS2019,新建Xamarin.Android项目,反复提示要安装Android SDK Build Tools 28.0.3,在弹出的窗口里点击接受协议,却无法安装SDK. 直接 ...

  5. Mark: 实现个toy版的脚手架(RPC)

    p.s.  这些小toy的规模都在几百~上千行代码量,但足以反映一个tool的核心思想. 包括: 一些中间件(消息队列.Netty) Spring的IoC容器:(自动依赖注入) —— 就是利用Java ...

  6. .Net Core实战教程(一):Linux下搭建项目

    .Net Core实战教程(一):Linux下搭建项目 附言 .net core 1.0的时候就开始关注了,一直没有用于项目.真正用于项目我是2.0开始使用的.这几年也总结出一些经验.最近有空就写出来 ...

  7. 构造命题公式的真值表--biaobiao88

    对给出的任意一个命题公式(不超过四个命题变元),使学生会用C语言的程序编程表示出来,并且能够计算它在各组真值指派下所应有的真值,画出其真值表. #include<iostream> usi ...

  8. 【Visio】亲测Visio2013激活,破解工具下载

    破解方法地址: https://blog.csdn.net/qq_38276669/article/details/85046615

  9. 英语chiltonite葡萄石chiltonite单词

    葡萄石Chiltonite 1.葡萄石能够促进血液循环,具有美容养颜的功效,非常适合女性佩戴,可以增加个人魅力,还能加强事业财运,凝聚财富气场. 2.绿色光对应人体心轮,对心脏,肺脏有效用,内涵的磁石 ...

  10. locust手机号批量注册性能测试

    from locust import TaskSet,task,HttpLocust from common.redisCon import redis_clusters import queue c ...