aqs(abstractqueuedsynchronizer)是java并发包(java.util.concurrent)中的一个核心组件,是构建锁和其他同步器的基础框架。
以下是对aqs的详细理解:
一、aqs的基本概念
aqs,全称为abstractqueuedsynchronizer,是一个抽象的队列式同步器。
它定义了一套多线程访问共享资源的同步器框架,为java并发同步组件提供统一的底层支持。
aqs是一个为各个同步组件提供基本框架的抽象类,其内部实现了同步状态的管理和线程的排队机制。
二、aqs的核心组件
- 同步状态:aqs使用一个
int类型的成员变量来表示同步状态。这个状态变量是线程共享的资源,通过内置的fifo队列(先进先出队列)来完成获取资源线程的排队工作。 - fifo队列:aqs的底层实现是一个双向链表,用于管理等待获取同步状态的线程。当线程无法获取同步状态时,会被加入到这个队列中等待。
- clh队列锁:aqs使用clh队列锁来实现线程的阻塞等待和唤醒机制。clh是一个虚拟的双向队列,即不存在队列实例,仅存在节点之间的关联关系。
三、aqs的工作原理
- 获取同步状态:当线程尝试获取同步状态时,会调用aqs的
tryacquire方法(对于独占模式)或tryacquireshared方法(对于共享模式)。如果成功获取到同步状态,则返回true;否则,线程会被加入到等待队列中。 - 释放同步状态:当线程释放同步状态时,会调用aqs的
tryrelease方法(对于独占模式)或tryreleaseshared方法(对于共享模式)。这些方法会修改同步状态的值,并可能唤醒等待队列中的线程。 - 线程排队与唤醒:等待获取同步状态的线程会被加入到fifo队列中,并按照队列的顺序等待。当某个线程释放同步状态时,会唤醒队列中的下一个线程来尝试获取同步状态。
四、aqs的资源共享方式
aqs支持两种资源共享方式:独占(exclusive)和共享(share)。
- 独占:只有一个线程能执行,如reentrantlock。独占锁又可分为公平锁和非公平锁。公平锁按照线程在队列中的排队顺序来获取锁,非公平锁则允许线程无视队列顺序直接去抢锁。
- 共享:多个线程可同时执行,如semaphore、countdownlatch、cyclicbarrier等。共享锁允许多个线程同时访问共享资源。
五、aqs的应用场景
aqs广泛应用于java并发编程中,是实现各种同步机制的基础。
例如,reentrantlock、semaphore、countdownlatch等同步器都是基于aqs实现的。
通过扩展aqs,开发者可以实现各种复杂的同步器,以满足不同的并发编程需求。
六、aqs的优缺点
优点:
- 提供了统一的同步器框架,简化了同步器的实现过程。
- 使用了fifo队列来管理等待线程,保证了线程的公平性。
- 提供了灵活的资源共享方式,支持独占和共享两种模式。
缺点:
- aqs是一个相对复杂的框架,需要开发者对其内部机制有一定的了解才能正确使用。
- 在某些情况下,aqs的性能可能不如一些定制的同步器。
七、aqs示例
import java.util.concurrent.locks.abstractqueuedsynchronizer;
import java.util.concurrent.locks.lock;
import java.util.concurrent.timeunit;
import java.util.concurrent.locks.condition;
import java.util.concurrent.locks.locksupport;
// 自定义锁类
class mylock implements lock {
// 静态内部类,继承aqs
private static class sync extends abstractqueuedsynchronizer {
// 是否处于独占模式
protected boolean isheldexclusively() {
return getstate() == 1;
}
// 尝试获取锁,当状态为0时获取锁成功
public boolean tryacquire(int acquires) {
assert acquires == 1; // 只允许获取1个单位的锁
if (compareandsetstate(0, 1)) {
setexclusiveownerthread(thread.currentthread());
return true;
}
return false;
}
// 尝试释放锁,将状态设置为0
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();
// 实现lock接口的方法
@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(unit.tonanos(time));
}
@override
public void unlock() {
sync.release(1);
}
@override
public condition newcondition() {
return sync.newcondition();
}
public static void main(string[] args) {
mylock lock = new mylock();
runnable task = () -> {
lock.lock();
try {
system.out.println(thread.currentthread().getname() + " 获取锁");
// 模拟任务执行
locksupport.parknanos(timeunit.seconds.tonanos(1));
} finally {
system.out.println(thread.currentthread().getname() + " 释放锁");
lock.unlock();
}
};
thread t1 = new thread(task, "thread-1");
thread t2 = new thread(task, "thread-2");
t1.start();
t2.start();
}
}mylock类:
- 实现
lock接口,内部包含一个静态内部类sync,该内部类继承自abstractqueuedsynchronizer。
sync类:
isheldexclusively:判断当前线程是否持有锁。tryacquire:尝试获取锁,如果当前状态为0(表示锁未被持有),则通过compareandsetstate方法将状态设置为1,并设置当前线程为独占线程。tryrelease:尝试释放锁,将状态设置为0,并清除独占线程。newcondition:创建一个条件变量。
mylock方法:
- 将
lock接口的方法代理到sync对象上。
main方法:
- 创建两个线程,每个线程尝试获取和释放锁,模拟任务执行。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论