首页 > 编程语言 >java并发编程——AQS

java并发编程——AQS

时间:2024-10-16 23:47:29浏览次数:9  
标签:java AQS 队列 编程 获取 线程 等待 资源

Java的AbstractQueuedSynchronizer (AQS) 是Java并发包(java.util.concurrent)中的一个非常重要的底层同步框架,它用于构建锁、信号量等同步器的基础组件。AQS 提供了一个通用的机制来管理线程之间的同步。通过AQS,Java中的各种同步器如ReentrantLockSemaphoreCountDownLatch等都是基于它实现的。

AQS的核心设计思想是通过一个 先进先出(FIFO)的等待队列 来管理那些尝试获取同步状态(通常是锁)的线程。当某个线程尝试获取锁资源时,如果资源可用,则立即获取;否则,线程会进入这个等待队列,直到资源可用。

下面是AQS的一些关键概念和设计要点:

1. 同步状态

AQS 使用一个 int 类型的成员变量来表示同步状态(State),这是整个同步控制的核心。同步状态的值表示某个资源的可用性。例如:

ReentrantLock 中,state = 0 表示锁没有被占用,而 state = 1 表示锁已被占用。

Semaphore 中,state 可以表示剩余的许可数量。

通过AQS的内置方法,可以对这个 state 进行原子操作:

getState():获取当前同步状态。

setState(int newState):设置同步状态。

compareAndSetState(int expect, int update):使用CAS操作设置状态,确保线程安全。

2. 独占模式和共享模式

AQS 支持两种模式的资源获取:

独占模式:在这种模式下,一个线程获取资源时,其他线程必须等待,直到该线程释放资源。例如,ReentrantLock 就是独占模式的实现。

共享模式:多个线程可以同时获取资源。例如,Semaphore 是共享模式的实现,允许多个线程同时获取多个许可。

AQS通过 tryAcquiretryRelease 等方法区分独占和共享操作:

tryAcquire(int arg)tryRelease(int arg):用于独占模式,分别尝试获取和释放锁。tryAcquireShared(int arg)tryReleaseShared(int arg):用于共享模式,分别尝试共享地获取和释放锁。

3. 等待队列

AQS的等待队列是一个 双向链表,当线程尝试获取资源失败时,AQS会将该线程加入到等待队列中,直到资源可用。等待队列中的节点类型是 Node,每个节点都封装了等待线程的信息。

节点中的一些关键字段:

thread:表示当前节点对应的线程。

prevnext:分别指向前驱和后继节点。

waitStatus:表示当前节点的等待状态,有以下几种状态:

     SIGNAL:表示当前节点的线程需要被唤醒。

     CANCELLED:表示线程等待超时或者被中断。

     CONDITION:表示节点在 Condition 队列中等待。

     PROPAGATE:表示下一次获取资源时需要唤醒后继节点。

等待队列的节点按照 FIFO 顺序排列,线程依次排队等待资源。

4. 独占模式下的获取和释放

在独占模式下,AQS 通过以下几个步骤来控制线程的获取和释放资源:

获取资源:当线程尝试获取资源时,首先调用 tryAcquire 方法判断当前资源是否可用。如果资源可用,线程直接获取资源并执行任务。如果资源不可用,线程会被封装为一个 Node 并加入到AQS的等待队列中,线程进入阻塞状态,等待其他线程释放资源。

释放资源:线程执行完任务后,会调用 release 方法释放资源。在 release 方法中,首先调用 tryRelease 方法释放资源。如果等待队列中有线程等待资源,AQS 会通过 unpark 操作唤醒等待队列中的第一个线程。

5. 共享模式下的获取和释放

共享模式的资源获取与独占模式略有不同,主要体现在多个线程可以同时获取资源:

获取资源:当线程尝试共享地获取资源时,首先调用 tryAcquireShared 方法判断资源是否可用。如果资源可用且能够满足多个线程同时获取的条件,线程将继续执行任务。如果资源不足,线程将被封装为 Node 加入到等待队列,等待资源可用。

释放资源:当一个线程释放资源时,AQS会调用 tryReleaseShared 方法释放资源。如果释放后的资源足够多,等待队列中的多个线程可能会被唤醒并继续执行任务。

6. Condition 条件队列

AQS 还为每个同步器提供了 Condition 支持,Condition 用于管理线程的等待和通知操作。通常,Condition 结合 await()signal() 来使用。Condition的实现也依赖于AQS的等待队列机制,但Condition有自己独立的等待队列。await() 方法会使当前线程进入条件队列,而 signal() 则负责唤醒条件队列中的线程。

7. 常用的AQS实现类

AQS是一个抽象类,不能直接使用。Java并发包中很多重要的同步器都是基于AQS实现的。常见的实现类包括:

ReentrantLock:基于AQS的独占锁实现。

ReentrantReadWriteLock:支持读写锁的实现。

Semaphore:基于AQS的共享模式实现。

CountDownLatch:基于AQS的计数器实现

8、例子

下面是一个简单的基于AQS实现的独占锁的例子:

import java.util.concurrent.locks.AbstractQueuedSynchronizer;  
import java.util.concurrent.locks.Lock;  
  
public class MyLock implements Lock {  
  
    // 自定义同步器  
    private static class Sync extends AbstractQueuedSynchronizer {  
        // 是否处于独占模式  
        protected boolean isHeldExclusively() {  
            return getState() == 1;  
        }  
  
        // 尝试获取锁  
        public boolean tryAcquire(int acquires) {  
            assert acquires == 1; // 只能是1  
            if (compareAndSetState(0, 1)) {  
                setExclusiveOwnerThread(Thread.currentThread());  
                return true;  
            }  
            return false;  
        }  
  
        // 尝试释放锁  
        protected boolean tryRelease(int releases) {  
            assert releases == 1; // 只能是1  
            if (getState() == 0) throw new IllegalMonitorStateException();  
            setExclusiveOwnerThread(null);  
            setState(0);  
            return true;  
        }  
  
        Condition newCondition() { return new ConditionObject(); }  
    }  
  
    // 将操作代理到Sync上  
    private final Sync sync = new Sync();  
  
    // 锁获取方法  
    public void lock() { sync.acquire(1); }  
  
    // 可中断的锁获取方法  
    public void lockInterruptibly() throws InterruptedException {  
        sync.acquireInterruptibly(1);  
    }  
  
    // 尝试获取锁  
    public boolean tryLock() { return sync.tryAcquire(1); }  
  
    // 超时尝试获取锁  
    public boolean tryLock(long timeout, java.util.concurrent.TimeUnit unit)  
            throws InterruptedException {  
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));  
    }  
  
    // 锁释放方法  
    public void unlock() { sync.release(1); }  
  
    // 提供Condition对象  
    public java.util.concurrent.locks.Condition newCondition() { return sync.newCondition(); }  
}

解释:

Sync类继承自AbstractQueuedSynchronizer,实现了tryAcquire和tryRelease方法,这两个方法分别用于尝试获取和释放锁。

tryAcquire方法通过compareAndSetState原子操作尝试将同步状态从0改为1,如果成功,则当前线程成为独占线程。

tryRelease方法将同步状态从1改为0,并清除独占线程。

MyLock类通过内部持有的Sync对象来实现Lock接口中的方法,从而提供了一个简单的独占锁实现。

标签:java,AQS,队列,编程,获取,线程,等待,资源
From: https://blog.csdn.net/sdg_advance/article/details/142931695

相关文章

  • Java中栈的数组存放方式
    今天课余时间学习了栈的数组存储方式:publicclassArrayStack{//创建一个名为stack的私有整型数组privateint[]stack;//创建一个指向栈顶的指针,私有,整型privateinttop;//构造函数初始化publicArrayStack(intcapacity){//capacity:容量;此处......
  • 分享字节跳动的免费AI编程助手(豆包MarsCode)
    邀请新用户注册登录&使用即可赚火星币,凭对应火星币可赢取京东卡和现金,最高可达1万元。接下来来手把手教你如何参与活动,推广赢好礼!合伙人推广赢好礼1.成为合伙人STEP1:点击https://www.marscode.cn/events/s/iBEnTPtB/,进入活动页面,点击【点击赚钱】按钮。 STEP2:活动页面......
  • 深入理解Java并发读写锁—ReentrantReadWriteLock
    ReentrantReadWriteLock使用场景ReentrantReadWriteLock是Java的一种读写锁,它允许多个读线程同时访问,但只允许一个写线程访问(会阻塞所有的读写线程)。这种锁的设计可以提高性能,特别是在读操作的数量远远超过写操作的情况下。在并发场景中,为了解决线程安全问题,我们通常会......
  • Java实现数据一致性
    在分布式系统中,数据一致性是一个核心问题。数据一致性确保了系统在并发操作和网络分区等情况下,数据的准确性和可靠性。Java作为一种广泛使用的编程语言,提供了多种机制来实现数据一致性。本文将探讨Java中实现数据一致性的方法和技术。1.数据一致性的定义在分布式系统中,数据......
  • 选择排序,插入排序,快速排序的java简单实现
    代码功能以下Java代码包含了三个排序算法的实现:选择排序(SelectionSort):通过不断选择剩余元素中的最小值来排序数组。插入排序(InsertionSort):通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。快速排序(QuickSort):使用分治法,通过一个基准值......
  • Java日总结 --- 数据库连接池&Druid使用
    简介:数据库连接池是一个容器,负责分配管理数据库连接;允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个好处有:资源重用,提升系统相应速度,避免数据库连接遗漏他是一个现成的数据库连接,只需要调用连接访问,不需要再次创建,当访问完毕后,会返还这个连接,可以提高效率;当连......
  • java学习10.16
    继续java图形化页面的学习,今天学的是页面的分区和布局importjava.awt.*;publicclass_1016{publicstaticvoidmain(String[]args){Frameframe=newFrame();frame.setBounds(500,500,300,300);frame.setAlwaysOnTop(true);//边界布局//BorderLay......
  • 编程语言-Object Pascal语言的面向对象扩展
    ObjectPascal是经典编程语言Pascal的一个扩展版本,引入了面向对象编程(OOP)的关键特性,如类与方法。这一革新性发展是在Pascal的创始人NiklausWirth的协商下,由LarryTesler带领的团队在苹果公司完成的。起源ObjectPascal的前身可以追溯到名为Clascal的语言。Clasc......
  • JAVA程序流程控制与基本例题
    程序的三种执行顺序1.分支结构(1)if分支结果packagecom.branch;publicclassifDemo{publicstaticvoidmain(String[]args){//目标:掌握了解if语句三种形式的用法,清楚其写法和应用场景。demo3();demo2();demo1();}......
  • 《GESP5级2309 单选题判断题》 解析(附加编程题)
    温馨提醒,以下解析为个人观点,还是得请大佬多多指教(可以喷,但不能说我是复制粘贴!)一、单选题(每题2分,共30分)1、近年来,线上授课变得普遍,很多有助于改善教学效果的设备也逐渐流行,其中包括⽐较常用的手写板,那么它属于哪类设备?()。A.输入B.输出C.控制D.记录这是一道定义判断的......