JAVA锁机制的底层实现 - AQS

AQS

2020128

14:29

 

目录

-

- 1. AQS是一个构建锁的框架。

- 2. AQS中的数据结构

- 3. AQS设计模式

- 4. 从ReentrantLock的加锁过程来理解AQS

概述

重要引用

'''

总而言之,上述除了 tryAcquire,是由RL定义的,其他都是AQS提供,AQS的获取资源,否则进入的逻辑即是acquire,它包含以下逻辑。

  • tryAcquire,需要实现,CAS申请资源
  • addWaiter,获取不到资源入队,不需要实现,注意入队也是CAS
  • acquireQueued,为队内资源分配资源,并且挂起活动的等待队列,去掉无效节点

'''

 

1. AQS是一个构建锁的框架。

其原理是基于CAS + 阻塞队列 —— 尝试获取,获取不到则进入队列等待。

AQS是区别于synchronized关键字的另一种锁实现机制,历史是,以前synchronized用mutex重量级互斥的悲观锁开销太大,因而Doug Lea大佬开发出基于CAS乐观锁的AQS。但后来synchronized进行了大量改进,例如四种级别的锁,现在它的效率通常来讲是最高的。

常用的AQS的锁有ReentrantLock,Semaphore。

2. AQS中的数据结构

想知道AQS如何利用简单CAS实现线程安全,先来看看它的数据结构,即挂载线程的节点Node和队列。

队列是类似双向链表的CLH链表,节点是普通的节点。

static final class Node {

    static final Node EXCLUSIVE = null;

 

    static final int CANCELLED = 1;

    // waitStatus的值,表示后继结点(对应的线程)需要被唤醒

    static final int SIGNAL = -1;

    // waitStatus的值,表示该结点(对应的线程)在等待某一条件

    static final int CONDITION = -2;

    /*waitStatus的值,表示有资源可用,新head结点需要继续唤醒后继结点(共享模式下,多线程并发释放资源,而head唤醒其后继结点后,需要把多出来的资源留给后面的结点;设置新的head结点时,会继续唤醒其后继结点)*/

    static final int PROPAGATE = -3;

    // 等待状态,取值范围,-3-2-101

 

    volatile int waitStatus;

 

    volatile Node prev; // 前驱结点

     volatile Node next; // 后继结点

    volatile Thread thread; // 结点对应的线程

    Node(Thread thread, Node mode) {     // Used by addWaiter

        this.nextWaiter = mode;

        this.thread = thread;

    }

}

队列是这个样子的。

3. AQS设计模式

模板设计模式,如下图,其实也非常简单——抽象类大部分东西给你写好,流程写好,剩下一些方法的不同需要你去实现。

对于AQS,实现AQS,需要覆盖的方法有:

  • tryAcquire(int):尝试使用CAS获取资源,资源由AQS中的标记变量标记,获取失败则入队,但是入队部分不需要我们实现。
  • tryRelease(int):释放资源。

其实上面只是独占式的锁——与之对应还有共享式,区别就是资源是一份还是多份,多人能不能同时使用资源(比如多人读就可以)。

4. ReentrantLock的加锁过程来理解AQS

ReentrantLock(RL)是最常用的独占锁,来看它的加锁过程。

  • RL调用lock方法,lock调用几个内部类的lock(实现AQS),最后调用父类的acquire方法
  • AQS: acquire方法,获取资源,获取不到则进入队列
    • 源码,核心代码

  • 首先调用RL: tryAcquire方法,尝试获取资源,由RL实现

  • 由该方法实现CAS修改AQS的资源量,首先它检查state,如果可以获取,则直接尝试用CAS获取,获取不到则return false,回到acquire
  • 该方法判断过程,即使多个进程同时CAS,只有一个能获得,其他会返回fasle,这也是实现独占资源的核心
  • addWaiter进入队列

  • 这里细节不重要,是入队指针的赋值,重要的只有注释部分,CAS入队以及enq自旋
  • enq的代码非常简单,创建或者循环入队

 

  • AQS: addWaiter入队以后返回node给aquireQueued
  • AQS: aquireQueued,给队列中排前面的节点(线程)分配资源

  • 看注释其中shouldParkAfterFailedAcquire的作用如注释所示
  • aquireQueued一般情况下分配完成会return false

 

总而言之,上述除了 tryAcquire,是由RL定义的,其他都是AQS提供,AQS的获取资源,否则进入的逻辑即是acquire,它包含以下逻辑。

  • tryAcquire,需要实现,CAS申请资源
  • addWaiter,获取不到资源入队,不需要实现,注意入队也是CAS
  • acquireQueued,为队内资源分配资源,并且挂起活动的等待队列,去掉无效节点
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值