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接口中的方法,从而提供了一个简单的独占锁实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值