AQS 详解及源码注释分析

AQS 详解及源码分析

1.概述

AQS 是什么?全称为 AbstractQueuedSynchronizer,是 JDK 中的一个抽象类。首先我们看看继承它的有哪些类:

基本上所有 JUC 并发包中的类都和它有关系,AQS 是用来构建锁或者其他同步器组件(读写锁等)的重量级基础框架及整个JUC体系的基石,通过内置的 FIFO 队列来完成对资源获取线程的排队工作,并通过一个 int 类型变量表示持有锁的状态。

通过 AQS 可以维持对共享资源的并发操作。

2.构成

通过这个 UML 图,可以得知 AQS 的来源和内部的组成,AQS 抽象类中还维护了两个内部类:Node 和ConditionObject 这两个内部类对于共享资源的维护其到关键的作用。Node(1) 用于数据封装、COnditionObject 用户状态维护,可以初步的认为 AQS 是一个由同步器和双向链表组成的 FIFO 队列。

如下图:

在这里插入图片描述

state 维护了对共享资源的状态,通过状态的定义,可以描述共享资源的状态,是被线程持有还是可以获取等状态。并且对 state 的更改具有原子性。

(1):这里是一个双向链表队列 FIFO,将线程封装成结点加入到这个队列中,Node 由 waitstatus + 前后指针组成,waitstatus 用来标识在这个队列中线程的一个等待状态。

3.LockSupport

3.1 概述

研究 AQS 的源码前,需要先了解 LockSupport,用于创建锁和其他同步类的基本线程阻塞原语,线程等待唤醒机制的升级版。

LockSupport 类使用了一种名为 permit(许可)的概念来做到阻塞和唤醒线程的功能,每一个线程都有一个许可,许可(permit)只有两个值1和0,每一个线程默认为0。

可以把许可看成是一种(0, 1)信号量(semaphore),但与 Semaphore(JUC 的一个辅助同步类)不同的是,许可的累加的上线为1。

JUC 中的辅助同步工具 Semaphore 中的许可是可以累加的,而 LockSupport 最多为1,无论发了多少张给指定线程

3.2 等待和唤醒方式

  • 使用 Object 类中的 wait() 方法让线程阻塞,notify()、allnotify() 唤醒阻塞线程
  • 使用 JUC 包中的 ReentrantLock 类中的 Condition 的 await() 方法让线程阻塞,single() 方法通知唤醒具体线程
  • 使用 LockSupport 类可以阻塞当前线程和唤醒指定被阻塞线程 park() 和 unpark()

3.3 park() 和 unpark()

3.3.1 park()

当线程默认调用 park() 时,因为当前 permit 的默认值为0,那么当前线程就会阻塞,直到其他线程(unpark)将当前线程的 permit 设置为1时,阻塞线程才会被唤醒。然后将消耗掉一个 permit。

底层通过 unsafe 类实现(操作系统原语)

3.3.2 unpark()

调用 unpark() 方法后,指定线程的 permit 许可就会加1,但是多次调用是不会使 permit 增加(不会累加),会自动唤醒因park被阻塞的线程,并返回。

底层通过 unsafe 类实现(操作系统原语)

3.3.3 实例
public class LockSupportDemo {
   
   
    public static void main(String[] args) {
   
        Thread a = new Thread(() -> {
   
            // 使线程阻塞
            LockSupport.park();
            // 等待被唤醒
            System.out.println(Thread.currentThread().getName()+"\t 被唤醒了");
        },"线程一");
       
        // 这里我们让唤醒的线程后启动,测试是否需要先阻塞后唤醒
        a.start();

        Thread b = new Thread(() -> {
   
            try {
   
                TimeUnit.SECONDS.sleep(2);
                // unpark方法需要传递一个指定的线程
                LockSupport.unpark(a);
                System.out.println(Thread.currentThread().getName()+"\t 解锁线程一");
            } catch (InterruptedException e) {
   
                System.out.println(e);
            }
        },"线程二");

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值