代码编织梦想

概要

由面及点,了解AQS的大体思想。以ReentrantLock为例,了解其使用

整体架构流程

AQS:AbstractQueuedSynchronizer,是Java并发编程中的一个重要组件,它提供了一种基于队列的同步机制,用于实现各种同步器(如锁、信号量等)。

它的核心思想是使用一个先进先出的等待队列来管理线程的竞争和等待。通过内部的state变量(int类型)来记录同步状态,并通过cas来实现对状态的原子更新。

所以AQS的两个核心:1.队列 2.加锁

以下是AQS的几个重要的内部变量:
	private transient volatile Node head;

    /**
     * Tail of the wait queue, lazily initialized.  Modified only via
     * method enq to add new wait node.
     */
    private transient volatile Node tail;

    /**
     * The synchronization state.
     */
    private volatile int state;

	private transient Thread exclusiveOwnerThread;
 三个重要的内部变量,state记录锁的状态,Node是一个双向的链表(等待队列),exclusiveOwnerThread是父类中的变量:独占线程
 
 ReentrantLock大致流程:当多个线程同时请求,通过cas的方式,只有一个线程拿到了锁,可以执行代码,其他的线程则被放入队列,并阻塞。当释放锁的时候,再从队列里唤醒等待的线程,继续通过cas去抢锁执行代码,如此反复

技术细节

1.ReentrantLock源码:

	private final Sync sync;

    /**
     * Base of synchronization control for this lock. Subclassed
     * into fair and nonfair versions below. Uses AQS state to
     * represent the number of holds on the lock.
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
如上,ReentrantLock没有直接继承AbstractQueuedSynchronizer,而是抽象出了一个内部类Sync实现AbstractQueuedSynchronizer。因为ReentrantLock内部有两种锁,公平锁和非公平锁,两种锁都继承Sync。以下详细描述公平锁:FairSync

2.AQS源码:

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
如上,当程序里对代码块进行加锁,也就是调用lock()方法,实则是调用了AbstractQueuedSynchronizer的acquire方法。先看第一个方法 tryAcquire(	arg),它由FairSync重写了。

3.ReentrantLock源码:

protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
如3:通过compareAndSetState()方法获取锁,然后通过setExclusiveOwnerThread方法,当独占线程变量设置成当前变量,以便重入锁。如果加锁成功,返回true,2就不再往下执行了。

4.AQS源码:
private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }



final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
如果抢锁失败,执行2中的acquireQueued(addWaiter(Node.EXCLUSIVE), arg)方法,如4,先调用addWaiter放入等待队列(放队列也使用了cas,因为存在多个线程同时往队列里放的情况,此处还用了一个循环,要确保一定放入队列成功),再调用acquireQueued方法,将当前线程阻塞,当调用unpark方法后,会去唤醒队列中的第一个线程,如此反复。

小结

个人觉得AQS设计的巧妙之处就是state,他可以实现很多东西。比如reentrantLock的重入锁,比如countDownLatch等等
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_34555668/article/details/132926060

java接入apiv3微信小程序支付(以java的eladmin框架为例)-爱代码爱编程

一、需要准备的资料         1.小程序AppID                 如:wx2e56f5******         2.商户号                 如:1641******         3.商户API私钥路径:什么是商户API证书?如何获取商户API证书? 获取文件如下图:            

mybatis:the error occurred while setting parameters;foreach语句不生效-爱代码爱编程

根本原因就是在参数上,列举一下可能的原因: 1.sql语句中的传的参数类型和数据库中不一致 2.#{}写成${} 3.也有说是在sql语句后加了“;”有影响的 本人sql语句如下: 该条语句的参数是list,list中存着

gof23设计模式之责任链模式-爱代码爱编程

1.概述 责任链模式又名职责链模式,为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。 2.结构

java 类加载机制-爱代码爱编程

文章目录 前言一、概述二、类的生命周期2.1.加载2.2.验证2.3.准备2.4.解析2.5.初始化 三、类的加载时机3.1、主动引用3.2.被动引用 四、类加载器五、双亲委派模型5.1双亲委派工作

mybatis-plus数据表操作条件构造器wrapper-爱代码爱编程

一、Wapper分类 Wrapper : 条件构造抽象类,最顶端父类 AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件 QueryWrapper : Entity 对象封装操作类,不

自定义全局异常-爱代码爱编程

1.自定义响应结果码 接口 public interface ResultCode { //操作是否成功,true为成功,false操作失败 boolean success(); //操作代码 int code(); //提示信息 String message(); } 2.自定义异常类 import

如何使用java进行安全测试?-爱代码爱编程

要使用Java进行安全测试,可以按照以下步骤进行: 确定测试目标:首先,明确要测试的应用程序或系统的安全目标和需求。确定要测试的安全方面,如身份验证、授权、输入验证、安全配置等。 了解安全测试知识:熟悉常见的安全漏洞和攻击方法,如跨站脚本攻击(XSS)、SQL注入、跨站点请求伪造(CSRF)等。理解这些漏洞和攻击方法的原理和实现方式,以便在测试过程中

java视频压缩亲测有效,无需插件-爱代码爱编程

依赖  <!-- 视频压缩 --> <dependency> <groupId>ws.schild</groupId> <artifactId>jave-core</artifactId> <version>3.0.0</version&g

springboot整合规则引擎-爱代码爱编程

Springboot整合Drools 规则引擎 1.添加maven 依赖坐标,并创建springboot项目 <!-- drools规则引擎 --> <dependency> <gr

【java】jdk8 jvm参数配置及说明-爱代码爱编程

参数 说明 1.堆内存参数设置 -Xms 或 -XX:InitialHeapSize=n 设置堆的初始值 指令1:-Xms2g 指令2:-XX:InitialHeapSize=2048m -Xmx 或 -XX:MaxHeapSize=n 设置堆区最大值 指令1:-Xmx2g 指令2: -XX:MaxHeapSize=2048m-XX:NewSi

java——break、continue(学习笔记)-爱代码爱编程

1.break(主要与switch搭配使用) 在任何循环语句的主体部分,均可用break控制循环的流程。break用于强行退出循环,不执行循环中剩余的语句。 2.continue 用在循环语句体中,用于终止某次循环过程

python 文件的读写操作-爱代码爱编程

文章目录 1. 文件对象1.1 文件打开方式1.1.1 打开文件1.1.2 关闭文件1.1.3 访问模式 1.2文件读取1.2.1 read()1.2.2 readline()1.2.3 readli