AQS 理解 及不可重入锁实现

news2024/11/24 23:13:25

由于目前水平有限,只是写出作者目前对 aqs的简单理解,有错误还请评论指证

AQS是什么

Aqs是java.util.concurrent 包下的一个抽象队列 同步器类,被简写为英文AQS,

我认为 可以把他理解 为一个 实现 自定义 锁的 一个具体的框架 ,我们可以根据这个 框架来实现出 自己的锁, 比如我们经常用到的  ReentrantLock,线程池  等 其他需要锁的类 内部类都实现了

AQS也就是 AbstractQueuedSynchronizer  

AQS解释

 AQS类的内部 是怎么来实现  原子性呢?  就是用Unsafe 来进行的, 

它的内部 维护了一个共享的变量 state ,  当satae 为0的时候,代表 没有被 其他线程 抢占到,如果state 为1

就代表 被其他线程抢占到了  这时候 其他线程会进入fifo 队列中等待

这么说太抽象了,我们直接上代码 来简单理解作者目前认为的 AQS

代码实现

首先 我们有一个 类 来继承了 AQS

final class Sync extends AbstractQueuedSynchronizer {
    @Override
    protected boolean tryAcquire(int arg) {
        //在 自定义锁中的lock方法去尝试给线程上锁的时候
        //根据传入的 arg 表示 来修改 AbstractQueuedSynchronizer类内部 的state 表示 进行上锁
        if (arg == 1) {//代表 要执行的是上锁操作
            if (compareAndSetState(0, 1)) {//原来0 是 锁未被其他线程使用
                // 修改为1 代表 上锁成功
                setExclusiveOwnerThread(Thread.currentThread());//设置 拥有锁的线程为 当前线程
                System.out.println(6666666);
                return true;
            }
        }
        return false;
    }

    @Override
    protected boolean tryRelease(int arg) {
        if (arg == 1) { //代表执行的是解锁操作
            if (getState() == 0) {//当前线程 进行解锁操作 发现 锁 并未被占用 所以抛出异常
                throw new IllegalMonitorStateException();
            }
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }
        return false;
    }

    @Override
    protected boolean isHeldExclusively() {//看当前线程是否 被占用
        return getState() == 1;
    }

    protected Condition newCondition() {
        return new ConditionObject();
    }
}

然后 我们 有一个自定义的锁 类, 内部 有一个该 Sync类的实现,通过操作 我们重写的AQS类的部分逻辑 来进行 加锁 操作

//不可重入锁
class MyLock implements Lock {

    static Sync sync = new Sync();

    @Override
    public void lock() {//不成功 进入等待队列
        sync.acquire(1);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {//可打断 不成功进入等待队列
        sync.acquireInterruptibly(1);
    }

    @Override
    public boolean tryLock() {//尝试上锁 立即返回
        return sync.tryAcquire(1);
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1, TimeUnit.SECONDS.toNanos(1));
    }

    @Override
    public void unlock() {
        sync.release(1);
    }

    @Override
    public Condition newCondition() {
        return sync.newCondition();
    }
}

看完这些代码 可能 有人会有疑惑,为什么 这个自定义的锁类,中调用的sync的方法 有的在 上面自定义的Sync类中没有,因为 我们调用的是其父类AQS的方法,我们目前自定义的sync类中的方法 就是 简单来模拟 一些AQS内部主要的加锁解锁的方法

我们再看 测试类

public class AqsTest {
    public static void main(String[] args) {
        MyLock myLock = new MyLock();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    myLock.lock();
                    System.out.println("上锁了t1");
                    sleep(1000);
//                   myLock.lock();
//                   System.out.println("上锁了t1重入");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } finally {
                    System.out.println("解锁了t1");
                    myLock.unlock();
                }
            }
        }, "t1").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("线程t2尝试获得锁");
                    myLock.lock();
                    System.out.println("上锁了t2");
                } finally {
                    System.out.println("解锁了t2");
                    myLock.unlock();
                }
            }
        }, "t2").start();
    }
}

代码分析

我们运行出来结果为

我们有的可能看到上面的代码实现就懵逼了,现在我们现在来看代码,稍微解读一下

首先 我们的 t1线程调用了 自定义锁的lock方法,开始去上锁

然后 到 自定义的 lock方法内部 去调用 sync 的acquire方法,我们点进去看acquire方法的实现

在aqs类内部,首先判断 这个线程是否获得锁,而这个 tryAcquire方法 走的逻辑 就是我们前面 重写的方法逻辑,先判断是否为上锁操作,然后用cas修改sate的状态值,然后设置 锁的拥有线程为当前线程,然后返回true,这时候 t2线程进来的话,t1线程由于没有释放锁tryacquire必然是false,这时候 就会走 aqs内部的acquire的方法,来进入等待队列

这时候 我们可能就对 aqs 有了一点点的理解,现在 我们再来看  ReentrantLock的源码实现,发现 好像用的是类似的逻辑

 总之 我目前认为aqs 就是 一个功能强大 锁实现框架

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2244994.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

nginx源码安装配置ssl域名

nginx源码安装 下载 wget http://nginx.org/download/nginx-1.24.0.tar.gz 解压 tar -zxvf nginx-1.24.0.tar.gz 下载openssl apt install openssl 安装nginx cd nginx-1.24.0 sudo apt-get install libpcre3 libpcre3-dev ./configure --prefix=/home/nginx24 --with-http_ss…

【笔记】Android Gradle Plugin配置文件相关说明-libs.versions.toml

版本号 文件路径:Project\gradle\libs.versions.toml 直接搜索versions.agp是找不到的,这是变量引用的写法,查询 agp版本可以直接查版本号。 [versions] agp "8.5.0-alpha08" junit "4.13.2" junitVersion "1.…

支付宝租赁小程序的优势与应用前景分析

内容概要 在这个快节奏的时代,租赁服务越来越成为大家生活中的一部分。而支付宝租赁小程序正是这个大潮流中的一颗璀璨明珠。通过简单易用的界面和强大的功能,这个小程序不仅让用户在租赁过程中获得了前所未有的便利,也为商家提供了新的商业…

三十一、构建完善微服务——API 网关

一、API 网关基础 系统拆分为微服务后,内部的微服务之间是互联互通的,相互之间的访问都是点对点的。如果外部系统想调用系统的某个功能,也采取点对点的方式,则外部系统会非常“头大”。因为在外部系统看来,它不需要也没…

深入理解 Redis跳跃表 Skip List 原理|图解查询、插入

1. 简介 跳跃表 ( skip list ) 是一种有序数据结构,通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。 在 Redis 中,跳跃表是有序集合键的底层实现之一,那么这篇文章我们就来讲讲跳跃表的实现原理。 2. …

MediaSession学习总结

1.框架预览 2.用法 2.1参考链接: MediaSession 简单使用 2.2 服务端要实现MediaBrowserService; 主要实现的功能: mPlaybackState new PlaybackStateCompat.Builder().setState(PlaybackStateCompat.STATE_NONE, currentPostion, 1.0f).…

学习大数据DAY61 宽表加工

目录 模型设计 加工宽表 任务调度: 大表 - 把很多数据整合起来 方便后续的明细查询和指标计算 模型设计 设计 建模 设计: excel 文档去编写 建模: 使用建模工具 PowerDesigner Navicat 在线画图工具... 把表结构给绘 制出来 共享\项目课工具\pd 加工宽表 数…

零基础入门Flink,掌握基本使用方法

Flink基本概念 首先来讲,Flink是一个面向数据流处理和批处理的分布式开源计算框架。 那么,流处理和批处理分别处理什么样的数据呢,这就涉及两个概念-无界流和有界流 无界流VS有界流 任何类型的数据都可以形成流数据,比如用户…

Linux设置以及软件的安装(hadoop集群安装02)

一、Linux的常见设置 1、设置静态IP vi /etc/sysconfig/network-scripts/ifcfg-ens33 如何查看自己的虚拟机的网关: 完整的配置(不要拷贝我的): TYPE"Ethernet" PROXY_METHOD"none" BROWSER_ONLY"no&…

数据中台方法论:数据汇聚

文章目录 一、数据汇聚概述二、 汇聚数据类型2.1 结构化数据2.2 半结构化数据2.3 非结构化数据 三、汇聚数据模式四、汇聚数据方法四、数据汇聚工具五、数据汇聚使用经验 数据小伙伴们,之前咱们长篇大论的聊聊过【数据中台建设方法论从0到1】,从数据中台…

【Maven】nexus 配置私有仓库配置【转】

介绍:【Maven】Nexus几个仓库的介绍-CSDN博客 一、仓库类型 proxy 远程仓库的代理,比如说nexus配置了一个central repository的proxy,当用户向这个proxy请求一个artifact的时候,会现在本地查找,如果找不到,则会从远程…

3C产品说明书电子化转变:用户体验、环保与商业机遇的共赢

在科技日新月异的当代社会,3C产品(涵盖计算机类、通信类和消费类电子产品)已成为我们日常生活中不可或缺的重要元素。与此同时,这些产品的配套说明书也经历了一场从纸质到电子化的深刻变革。这一转变不仅体现了技术的飞速进步&…

【YOLOv8】安卓端部署-2-项目实战

文章目录 1 准备Android项目文件1.1 解压文件1.2 放置ncnn模型文件1.3 放置ncnn和opencv的android文件1.4 修改CMakeLists.txt文件 2 手机连接电脑并编译软件2.1 编译软件2.2 更新配置及布局2.3 编译2.4 连接手机 3 自己数据集训练模型的部署4 参考 1 准备Android项目文件 1.1…

虚拟网卡驱动和DM9000C移植

网卡驱动程序框架 网卡驱动程序“收发功能”: 只要把上层的数据发给网卡,从网卡来的数据构造成包给上层即可。网卡只需要 “socket”编程,不需要打开某设备。 驱动程序都是以面向对象的思想写的,都有相关的结构体。 编程步骤 …

Vue3 + Vite 项目引入 Typescript

文章目录 一、TypeScript简介二、TypeScript 开发环境搭建三、编译方式1. 自动编译单个文件2. 自动编译整个项目 四、配置文件1. compilerOptions基本选项严格模式相关选项(启用 strict 后自动包含这些)模块与导入相关选项 2. include 和 excludeinclude…

Cyberchef使用功能之-多种压缩/解压缩操作对比

cyberchef的compression操作大类中有大量的压缩和解压缩操作,每种操作的功能和区别是什么,本章将进行讲解,作为我的专栏《Cyberchef 从入门到精通教程》中的一篇,详见这里。 关于文件格式和压缩算法的理论部分在之前的文章《压缩…

Istio分布式链路监控搭建:Jaeger与Zipkin

分布式追踪定义 分布式追踪是一种用来跟踪分布式系统中请求的方法,它可以帮助用户更好地理解、控制和优化分布式系统。分布式追踪中用到了两个概念:TraceID 和 SpanID。 TraceID 是一个全局唯一的 ID,用来标识一个请求的追踪信息。一个请求…

Linux修改/etc/hosts不起作用(ping: xxx: Name or service not known)的解决方法——开启NSCD

​ 问题描述 起因是我在实验室云资源池的一台虚拟机(CentOS 8.5)上的/etc/hosts文件中为Fabric网络节点的域名指定了IP: IP可以ping通,但是ping域名时提示ping: xxx: Name or service not known。 问题本身应该是Linux通用的&a…

Python中Tushare(金融数据库)入门详解

文章目录 Python中Tushare(金融数据库)入门详解一、引言二、安装与注册1、安装Tushare2、注册与获取Token 三、Tushare基本使用1、设置Token2、获取数据2.1、获取股票基础信息2.2、获取交易日历2.3、获取A股日线行情2.4、获取沪股通和深股通成份股2.5、获…

【网络】网络抓包与协议分析

网络抓包与协议分析 一. 以太网帧格式分析 这是以太网数据帧的基本格式,包含目的地址(6 Byte)、源地址(6 Byte)、类型(2 Byte)、数据(46~1500 Byte)、FCS(4 Byte)。 Mac 地址类型 分为单播地址、组播地址、广播地址。 单播地址:是指第一个字节的最低位…