理解Java并发底层之AQS实现

本文详细介绍了Java并发基础中的AQS(AbstractQueuedSynchronizer)实现原理及其在并发工具中的应用。AQS是Java并发编程的基础,用于实现包括ReentrantLock、ReentrantReadWriteLock在内的各种并发工具。文章从名词解释出发,讲解了AQS、CAS和线程中断的概念,并详细阐述了对象锁的显式获取、释放、可重入、可共享及公平、非公平特性。此外,还介绍了Condition接口、CLH队列以及AQS如何基于这些机制实现线程的阻塞和唤醒。通过对AQS的理解,读者可以更深入地掌握Java并发编程的核心机制。
摘要由CSDN通过智能技术生成

jdk的JUC包(java.util.concurrent)提供大量Java并发工具提供使用,基本由Doug Lea编写,很多地方值得学习和借鉴,是进阶升级必经之路

本文从JUC包中常用的对象锁、并发工具的使用和功能特性入手,带着问题,由浅到深,一步步剖析并发底层AQS抽象类具体实现

名词解释

1 AQS

AQS是一个抽象类,类全路径java.util.concurrent.locks.AbstractQueuedSynchronizer,抽象队列同步器,是基于模板模式开发的并发工具抽象类,有如下并发类基于AQS实现:

2 CAS

CAS是Conmpare And Swap(比较和交换)的缩写,是一个原子操作指令

CAS机制当中使用了3个基本操作数:内存地址addr,预期旧的值oldVal,要修改的新值newVal 更新一个变量的时候,只有当变量的预期值oldVal和内存地址addr当中的实际值相同时,才会将内存地址addr对应的值修改为newVal

基于乐观锁的思路,通过CAS在不断尝试和比较,可以对变量值线程安全地更新

3 线程中断

线程中断是一种线程协作机制,用于协作其他线程中断任务的执行

当线程处于阻塞等待状态,例如调用了wait()、join()、sleep()方法之后,调用线程的interrupt()方法之后,线程会马上退出阻塞并收到InterruptedException;

当线程处于运行状态,调用线程的interrupt()方法之后,线程并不会马上中断执行,需要在线程的具体任务执行逻辑中通过调用isInterrupted() 方法检测线程中断标志位,然后主动响应中断,通常是抛出InterruptedException

对象锁特性

下面先介绍对象锁、并发工具有哪些基本特性,后面再逐步展开这些特性如何实现

1 显式获取

以ReentrantLock锁为例,主要支持以下4种方式显式获取锁

  • (1) 阻塞等待获取

ReentrantLock lock = new ReentrantLock();
// 一直阻塞等待,直到获取成功
lock.lock();
  • (2) 无阻塞尝试获取

ReentrantLock lock = new ReentrantLock();
// 尝试获取锁,如果锁已被其他线程占用,则不阻塞等待直接返回false
// 返回true - 锁是空闲的且被本线程获取,或者已经被本线程持有
// 返回false - 获取锁失败
boolean isGetLock = lock.tryLock();
  • (3) 指定时间内阻塞等待获取

ReentrantLock lock = new ReentrantLock();
try {
    // 尝试在指定时间内获取锁
    // 返回true - 锁是空闲的且被本线程获取,或者已经被本线程持有
    // 返回false - 指定时间内未获取到锁
    lock.tryLock(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
    // 内部调用isInterrupted() 方法检测线程中断标志位,主动响应中断
    e.printStackTrace();
}
  • (4) 响应中断获取

ReentrantLock lock = new ReentrantLock();
try {
    // 响应中断获取锁
    // 如果调用线程的thread.interrupt()方法设置线程中断,线程退出阻塞等待并抛出中断异常
    lock.lockInterruptibly();
} catch (InterruptedException e) {
    e.printStackTrace();
}

2 显式释放

ReentrantLock lock = new ReentrantLock();
lock.lock();
// ... 各种业务操作
// 显式释放锁
lock.unlock();

3 可重入

已经获取到锁的线程,再次请求该锁可以直接获得

4 可共享

指同一个资源允许多个线程共享,例如读写锁的读锁允许多个线程共享,共享锁可以让多个线程并发安全地访问数据,提高程序执效率

5 公平、非公平

公平锁:多个线程采用先到先得的公平方式竞争锁。每次加锁前都会检查等待队列里面有没有线程排队,没有才会尝试获取锁。非公平锁:当一个线程采用非公平的方式获取锁时,该线程会首先去尝试获取锁而不是等待。如果没有获取成功,才会进入等待队列

因为非公平锁方式可以使后来的线程有一定几率直接获取锁,减少了线程挂起等待的几率,性能优于公平锁

AQS实现原理

1 基本概念

(1) Condition接口

类似Object的wait()、wait(long timeout)、notify()以及notifyAll()的方法结合synchronized内置锁可以实现可以实现等待/通知模式,实现Lock接口的ReentrantLock、ReentrantReadWriteLock等对象锁也有类似功能:

Condition接口定义了await()、awaitNanos(long)、signal()、signalAll()等方法,配合对象锁实例实现等待/通知功能,原理是基于AQS内部类ConditionObject实现Condition接口,线程await后阻塞并进入CLH队列(下面提到),等待其他线程调用signal方法后被唤醒

(2) CLH队列

CLH队列,CLH是算法提出者Craig, Landin, Hagersten的名字简称

AQS内部维护着一个双向FIFO的CLH队列,AQS依赖它来管理等待中的线程,如果线程获取同步竞争资源失败时,会将线程阻塞,并加入到CLH同步队列;当竞争资源空闲时,基于CLH队列阻塞线程并分配资源

CLH的head节点保存当前占用资源的线程,或者是没有线程信息,其他节点保存排队线程信息

CLH<

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值