首页 > 编程语言 >[Java并发]AQS的可重入性

[Java并发]AQS的可重入性

时间:2024-10-03 23:12:17浏览次数:8  
标签:重入性 加锁 Java AQS lock ReentrantLock state 线程

在Java中,AQS(AbstractQueuedSynchronizer,抽象队列同步器)通过设计一个独占和共享的同步机制,提供了可重入锁的实现。AQS 的可重入性主要依赖于它对线程状态的跟踪。具体来说,可重入性是指同一个线程在获得锁之后可以多次进入(加锁多次),而不引发死锁。这是通过一个“重入计数器”来实现的。

下面是AQS实现可重入性的核心机制:

1. 线程持有状态(state)

AQS 使用一个 state 变量来表示锁的持有状态。在独占锁(如 ReentrantLock)的情况下,state 变量记录锁被持有的次数。AQS 的设计允许同一个线程多次获取锁,每次获取锁时,state 变量会递增,而每次释放锁时,state 变量会递减,直到 state 变为 0 时,锁才会真正释放。

2. 当前持有锁的线程

AQS 通过内部的一个线程引用 exclusiveOwnerThread 来跟踪当前持有锁的线程。当一个线程尝试获取锁时,AQS 会检查当前线程是否已经持有锁(即 exclusiveOwnerThread == currentThread)。如果是同一个线程,则允许该线程再次获取锁,表示“可重入”。

3. 可重入的判断过程

  • 当一个线程第一次获取锁时,AQS 会将 exclusiveOwnerThread 设置为该线程,并将 state 从 0 设置为 1。
  • 如果同一线程再次尝试获取锁,AQS 看到 exclusiveOwnerThread 已经是当前线程,于是允许锁的重入,并将 state 递增。
  • 当线程释放锁时,AQS 会减少 state 的值。只有当 state 减为 0 时,AQS 才会将 exclusiveOwnerThread 置为 null,表示锁已完全释放。

4. 代码示例

ReentrantLock 为例,ReentrantLock 是基于 AQS 实现的可重入锁:

class ReentrantLockExample {
    private final ReentrantLock lock = new ReentrantLock();

    public void performTask() {
        lock.lock(); // 第一次加锁
        try {
            // 执行任务
            anotherMethod(); // 同一线程可以再次加锁
        } finally {
            lock.unlock(); // 第一次解锁
        }
    }

    public void anotherMethod() {
        lock.lock(); // 第二次加锁
        try {
            // 执行其他任务
        } finally {
            lock.unlock(); // 第二次解锁
        }
    }
}

在这个例子中,performTaskanotherMethod 都会加锁,而由于是同一线程,所以 lock 会被允许多次加锁。

5. 锁的释放

当线程多次加锁时,每次加锁都对应一次释放。只有当释放次数与加锁次数相等时,锁才会真正释放,允许其他线程获取。

总结

AQS 的可重入性主要是通过 state 变量和 exclusiveOwnerThread 来实现的。它通过跟踪线程加锁的次数以及锁的当前拥有者,确保同一个线程可以多次进入锁区域,而不会导致死锁。这是 Java 中许多同步类(如 ReentrantLock)的基础。

标签:重入性,加锁,Java,AQS,lock,ReentrantLock,state,线程
From: https://www.cnblogs.com/DCFV/p/18446129

相关文章

  • [JavaScript] this 关键字
    全局作用域在浏览器中,如果在全局作用域下使用this,它将指向window对象;在Node.js环境中,则指向global对象。方法调用当一个函数作为对象的方法被调用时,this会指向该对象。constobj={name:"Alice",greet:function(){console.log(`Hello,${this.name}`)......
  • 从 JavaScript 到 OCaml:浅浅浅总结一下函数式编程
    背景这几天突击了一下Cornell的cs3110;抽了两个下午刷完了Chapter3,4,5的课后习题,很有感触。结合自己浅薄的函数式编程理解和贫瘠的JavaScript/TypeScript开发经历,总结一下自己第一阶段的函数式编程学习经历。......
  • java日总结24-10-3:mysql的基础知识
    今日学习javaweb1、了解了javaweb的概况与数据库的相关概念2、安装了mysqlMySQL的学习:1、SQL的简介:是一门操作关系型数据库的编程语言2、SQL的通用语法:单行注释:--注释内容或#注释内容;多行注释:/注释内容/3、SQL的分类:一、DDL操作数据库:创建数据库:判断是否存在创建:c......
  • 盘点五大热门JavaScript图表库,助你高效开发!
    1、Chart.jsChart.js是一个简单、灵活的图表库,支持8种图表类型(如折线图、柱状图、饼图等)。它使用HTML5Canvas元素来渲染图表,并且有良好的文档和社区支持。使用方法:npminstallchart.jsvue-chartjs然后在你的Vue组件中使用:<template><div><line-chart:data......
  • 掌握防抖与节流:如何用JavaScript封装通用函数
    在日常前端开发中,我们经常会遇到一些频繁触发的事件,如窗口调整大小、滚动条滚动、输入框输入等。为了提高页面性能和用户体验,我们需要对这些事件进行优化。本文将介绍如何使用JavaScript封装通用的防抖和节流函数。一、什么是防抖(Debounce)和节流(Throttle)?防抖(Debounce):当持续......
  • Java面向对象第四章方法重写与多态练习题
    练习1:使用重写优化电子宠物系统需求说明使用方法重写优化电子宠物系统,实现如下效果 packagecom.hz.ch02;/***猫类*@author26255**/publicclassCatextendsFather{ privateStringsex; publicCat(Stringname,inthealth,intlove,Stringse......
  • JavaWeb基础-学习笔记02
    02MySQL数据库、SQL、数据库设计、事务、JDBC1.MySQL数据库1.1MySQL的安装:包含两种安装方式,解压包安装、压缩包安装。通过解压包安装:下载压缩包后进行解压、添加my.ini初始化配置文件、添加环境变量;添加mysqld服务、启动该服务。期间遇到的一些问题和解决方案:找......
  • Java流程控制-基础语法及运用
    基本语法Scannerin=newScanner(System.in);通过Scanner类的next()与nextLine()方法获取输入的字符串,再读取前还可以进行判断是否输入了数据。next()1.一定要读取到有效字符后才可以结束输入2.对输入有效字符之前遇到的空白,next()方法会自动将其去掉3.只有输入有效字符后才可以将其......
  • 探索 java 中的各种锁
    Java8+- 序章一直听说java的各种锁、(线程)同步机制,一直没有深入探索。最近多看了几篇博文,算是理解的比较深入一些了,特写本文做个记录。ben发布于博客园 锁是什么?一种用于多个线程运行时协调的机制。作用如下:ben发布于博客园1、用于多线程之间同步,比如,执行顺序先后......
  • Java内存区域与分配策略
    1.运行时数据区Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域有各自的用途,创建和以及销毁的时间,有的区域随着虚拟机进程的启动而一直存在,有的区域则是依赖用户线程的启动结束而建立和销毁。1.1程序计数器(线程私有)程序计数器(Pro......