在并发编程领域,AQS号称是并发同步组件的基石,很多并发同步组件都是基于AQS实现,所以想掌握好高并发编程,你需要掌握好AQS。
本篇主要通过对AQS的实现原理、数据模型、资源共享方式、获取锁的过程,让你对AQS的整体设计有清晰了解,让你迈出高并发编程的第一步。
本节课重点会谈到以下8点:
- AQS的核心概念
- AQS资源共享方式
- AQS核心成员
- AQS数据模型
- AQS实现原理
- AQS锁核心源码剖析
- AQS锁的获取原理与流程图
- AQS的总结
AQS
AQS(AbstractQueuedSynchronizer)就是一个抽象的队列同步器,AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它。
AQS的主要作用是为Java中的并发同步组件提供统一的底层支持,比如大家熟知的:
- ReentrantLock
- Semaphore
- CountDownLatch
- CyclicBarrier
等并发类均是基于AQS来实现的。
AQS的核心概念
AQS(AbstractQueuedSynchronizer)就是一个抽象的队列同步器,AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它 AQS是一个Java提供的底层同步工具类,用一个volatile int类型的变量表示同步状态,并提供了一系列的CAS操作来管理这个同步状态。
AQS的主要作用是为Java中的并发同步组件提供统一的底层支持,比如:
ReentrantLock(重入锁)
Semaphore(信号量)
CountDownLatch(倒计时)
同步状态
AQS中维持一个全局的volatile int state来表示同步状态,线程通过修改(加/减指定的数量)码是否成功来决定当前线程是否成功获取到同步状态。
CLH同步队列
维护了一个FIFO双向队列,该队列就是CLH同步队列,来完成线程等待排队。
CLH同步队列是AQS的核心,用来完成同步状态的管理,当线程获取同步状态失败时,AQS会将当前线程以及等待状态等信息构造成一个节点并加入到同步队列,同时会阻塞当前线程。
备注:CLH(Craig, Landin, andHagersten)根据三个人的名字命名的队列
AQS资源共享方式
AQS支持两种获取同步状态的模式既独占式和共享式。
独占锁
顾名思义,独占式模式同一时刻只允许一个线程获取同步状态。
典型代表:ReentrantLock采用独占模式
ReentrantLock还可以分为公平锁和非公平锁:
- 公平锁:按照线程在队列中的排队顺序,先到者先拿到锁
- 非公平锁:当线程要获取锁时,无视队列顺序直接去抢锁,谁抢到就是谁的
共享锁
共享模式则允许多个线程同时获取。
典型代表:Semaphore、CountDownLatch、CyclicBarrier等
AQS的数据模型
课后作业
AQS是在并发编程里经常考察的题目,需要重点掌握,因为后续的同步组建都是基于AQS来实现锁的操作的。
来一道大厂经常面试的题目:
AQS是抽象队列同步器,是java并发的底层基石。
AQS的核心模型包含2个: 一个volatile的state变量,一个fifo的队列。state状态的改变是通过cas操作来执行,保障了操作的原子性,而volatile修饰符保障了多线程间的state状态的可见性。
reentrantlock是基于AQS的独占锁实现,countdownlatch、cyclebarrier等是基于aqs的共享锁实现。
获取锁的流程图如下:
(ps:老师,自旋那里,我看了代码,自旋第二次,就阻塞了,不是一直自旋)
上传图,提示无权限,等弄好了,我再回复补发图
是的,你这个更加准确,自旋这里会发生两次,两次后会进入阻塞状态:
第一次:前驱的状态为0,执行compareAndSetWaitStatus后返回false。
第二次:才能进入if (ws == Node.SIGNAL)这个分支,才能返回true。
如果当前线程一直不能获取到锁,死循环保证了最终一定能设置前驱为SIGNAL成功。
这里关于SIGNAL与阻塞的理解,可以用闹钟与睡觉这个比喻更容易来理解:
比如:前驱节点已经设置了SIGNAL,表示闹钟已经设好,可以安心睡觉(阻塞)。
补充图
AQS的底层实现:
一个全局的volitile int state来表示同步状态,用volatile来保证可见性,用CAS来保证原子性
CLH同步队列来完成线程等待排队
基于AQS的同步组件有:
ReentrantLock(独占锁)
Semaphore、CountDownLatch、CyclicBarrier(共享锁)
图:
手画练习马上就来了 ✗咧嘴笑✗
AbstractQueuedSynchronizer,抽象队列同步器,是jdk为同步器提供的一个框架。
在AQS中,主要有三个变量,head,tail,state,这三个变量均使用volatile关键字进行修饰,保证了多线程情况下的可见性。
AQS的使用state来标记同步状态,在使用过程中设置state是通过CAS操作来实现,从而保证了原子性。AQS使用CLH(FIFO)队列来存储线程的等待节点,当线程获取同步状态失败时,会将当前线程和等待状态等信息构造成一个节点加入到同步队列的队尾,同时会阻塞当前线程。
ReentrantLock、Semaphore、CountDownLatch等都是通过AQS方式实现。
图片上传不知道怎么用
图片可以结合文字正常提交,我这边刚看到了,没有问题。
嗯,这个图片上传了以后在文本框里没有回显,所以不能及时知道有没有上传成功。现在知道怎么使用了。