AQS原理

简单介绍

AQS即AbstractQuenedSynchronizer,中文名称抽象队列同步器,定义了一套多线程访问共享资源的同步框架,许多同步类的实现都依赖于它,像ReentrantLock、ReentrantReadWriteLock、Semaphore、CountDownLatch等。

实现原理

如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制 AQS 是基于CLH 锁实现的。 CLH 锁其实是对自旋锁的一种改进,是一个虚拟的双向队列,暂时获取不到锁的线程将被加入到该队列中。AQS 将每条请求共享资源的线程封装成一个 CLH 队列锁的一个结点(Node)来实现锁的分配。在 CLH 队列锁中,一个节点表示一个线程,它保存着线程的引用(thread)、 当前节点在队列中的状态(waitStatus)、前驱节点(prev)、后继节点(next)。

也就是说AQS中维护了一个volicate int state(共享资源)和一个FIFO的线程等待队列。

这里volatile 能够保证多线程下的可见性,当state=1 则代表当前对象锁已经被占有,其他线程来加锁时则会失败,加锁失败的线程会被放入一个FIFO的等待队列中,并且会挂起,等待其他获取锁的线程释放锁才能够被唤醒,并且state值可以大于一,代表锁是可重入的。此外,对state的操作都是通过CAS来保证其修改并发性。

以可重入的互斥锁ReentrantLock 为例,它的内部维护了一个state 变量,用来表示锁的占用状态。state 的初始值为 0,表示锁处于未锁定状态。当线程 A 调用 lock() 方法时,会尝试通过 tryAcquire() 方法独占该锁,并让 state 的值加 1。如果成功了,那么线程 A 就获取到了锁。如果失败了,那么线程 A 就会被加入到一个等待队列(CLH 队列)中,直到其他线程释放该锁。假设线程 A 获取锁成功了,释放锁之前,A 线程自己是可以重复获取此锁的)。这就是可重入性的体现:一个线程可以多次获取同一个锁而不会被阻塞。但是,这也意味着,一个线程必须释放与获取的次数相同的锁,才能让 state 的值回到 0,也就是让锁恢复到未锁定状态。只有这样,其他等待的线程才能有机会获取该锁。

节点状态

节点状态waitStatus,需要保证可见性,用volicate实现,waitStatus标识Node节点的状态,共有5种取值CANCELLED、SIGNAL、CONDITION、PROPAGATE、0。

  • CANCELLED(1): 表示节点已取消调度,当timeout或被中断时,会触发变更为此状态,进入此状态的节点将不会再变化。
  • SIGNAL(-1): 表示后继节点在等待当前节点的唤醒。后继节点入队时,会将前继节点的状态更新为SIGNAL。
  • CODINTION(-2): 表示节点等待在Condition上,当其他线程调用了Condition的singal()方法后,Condition状态的节点将从等待队列转移到同步队列中。
  • PROPAGATE(-3): 共享模式下,前继结点不仅会唤醒其后继结点,同时也可能会唤醒后继的后继结点。
  • 0:新节点入队时的默认状态。

如何自定义实现AQS

同步器的方法是基于模板方法实现的,我们只需要继承AbstractQueuedSynchronizer并且重写指定方法,然后再使用时将AQS组合在自定义同步组件的视线中,并调用其模板方法,这些模板方法就会自动调用使用者的重写方法。

自定义同步器时需要重写以下方法:

Java 复制代码
//独占方式。尝试获取资源,成功则返回true,失败则返回false。
protected boolean tryAcquire(int)
//独占方式。尝试释放资源,成功则返回true,失败则返回false。
protected boolean tryRelease(int)
//共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
protected int tryAcquireShared(int)
//共享方式。尝试释放资源,成功则返回true,失败则返回false。
protected boolean tryReleaseShared(int)
//该线程是否正在独占资源。只有用到condition才需要去实现它。
protected boolean isHeldExclusively()
相关推荐
夜半被帅醒2 分钟前
MySQL 数据库优化详解【Java数据库调优】
java·数据库·mysql
万亿少女的梦1688 分钟前
基于Spring Boot的网络购物商城的设计与实现
java·spring boot·后端
醒了就刷牙36 分钟前
黑马Java面试教程_P9_MySQL
java·mysql·面试
m0_7482336442 分钟前
SQL数组常用函数记录(Map篇)
java·数据库·sql
编程爱好者熊浪2 小时前
JAVA HTTP压缩数据
java
吴冰_hogan2 小时前
JVM(Java虚拟机)的组成部分详解
java·开发语言·jvm
开心工作室_kaic2 小时前
springboot485基于springboot的宠物健康顾问系统(论文+源码)_kaic
spring boot·后端·宠物
0zxm2 小时前
08 Django - Django媒体文件&静态文件&文件上传
数据库·后端·python·django·sqlite
白宇横流学长3 小时前
基于java出租车计价器设计与实现【源码+文档+部署讲解】
java·开发语言
数据小爬虫@5 小时前
Java爬虫实战:深度解析Lazada商品详情
java·开发语言