JUC-AQS面试(AbstractQuenedSynchronizer)

本文深入解析了Java并发编程中的核心组件AbstractQueuedSynchronizer(AQS),阐述了其在锁和同步器中的应用,包括独占模式和共享模式。AQS通过CLH队列实现线程的阻塞与唤醒,并介绍了CountDownLatch、Semaphore和CyclicBarrier等并发工具类的使用。此外,还提到了JDK8的StampedLock,它优化了读性能并支持乐观读锁。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java.util.concurrent(J.U.C)大大提高了并发性能,AQS是JUC的核心,是阻塞式锁相关的同步器工具的框架,是一个主要用来构建锁和同步器的抽象类。全称AbstractQuenedSynchronizer 意思就是抽象队列同步器。

AQS特点

  • 用state属性来表示资源的状态(分独步模式和共享模式),子类需要定义如何维护这个状态,控制如果获取锁和释放锁

    • getState-获取state状态

    • setState-设置state状态

    • compareAndSetState-乐观锁机制设置state状态

    • 独占模式:只有一个线程能访问资源

    • 共享模式:可以允许多个线程访问资源

  • 提供基于FIFO等待队列,类似Monitor的EntryList

  • 条件变量来实现等待,唤醒机制

子类主要实现这样的一些方法(默认抛出UnsupportedOperationException)

  • tryAcquire //尝试获取锁

  • tryRelease //尝试释放锁

  • tryAcquireShared

  • tryReleaseShared

  • isHeldExclusively //是否持有独占锁

获取锁的姿势:

//如果获取锁失败
if(!tryAcquire(arg)){
	//入队,可以选择阻塞当前线程 
}

释放锁的姿势:

//如果释放锁失成功
if(tryRelease(arg)){
	//让阻塞的线程恢复运行
}

AQS原理

AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制

这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中,

CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。AQS 是将每条请求共享资源的线程封装成一个 CLH 锁队列的一个结点(Node)来实现锁的分配。

CountDownLatch (倒计时器)

用来控制一个或者多个线程等待多个线程。

它默认构造 AQS 的 state 值为 count ,维护了一个计数器 cnt,每次调用 countDown() 方法会让计数器的值减 1,减到 0 的时候,那些因为调用 await() 方法而在等待的线程就会被唤醒。

StampedLock

JDK8加入,进一步优化读性能,在读锁或者写锁的时候都需要配合【戳】使用

不支持条件变量、不支持可重入

//加解读锁
long stamp = lock.readLock();
lock.unlockRead(stamp);
//加解写锁
long stamp = lock.writeLock();
lock.unlockWrite(stamp);

乐观读,StampedLock支持tryOptimisticRead()方法,读取完毕后要再一次进行戳检验,如果校验通过,表示这期间确实没有写操作,数据可以安全使用,若校验没通过需要重新获取读锁确保数据安全  

long stamp = lock.tryOptimisticRead();//戳
//验戳
if(!lock.validate(stamp)){
	//锁升级
}
private int data;
private final StampedLock lock = new StampedLock();
public DataContainerStamped(int data){
    this.data = data;
}
public int read(){
    long stamp = lock.tryOptimisticRead();
    sleep(1);
    //乐观读
    if(lock.validate(stamp)){
        return data;
    }
    //锁升级,读锁
    try{
        stamp = lock.readLock();
        sleep(1);
        return data;
    }finally{
        lock.unlockRead(stamp);
    }
}
public void write(){
    long stamp = lock.writeLock();
    try{
        sleep(2);
        this.data = newData
    }finally{
        lock.unlockWriter(stamp);
    }
}

Semaphore

信号量, 可以控制对互斥资源的访问线程数 ,限制同步队列的容量

 public static void main(String[] args) {
        //创建semaphore对象
        Semaphore semaphore = new Semaphore(3);
        //创建线程 同时运行
        for (int i = 0; i < 10; i++) {
            int finalI = i;
            new Thread(()->{
                //获得许可
                try {
                    semaphore.acquire();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try {
                   System.out.println(finalI+"running....");
					sleep(1);
                   System.out.println(finalI+ "end....");
                    } finally {
                        semaphore.release();
                }
            }).start();
        }
    }

CyclicBarrier

用来控制多个线程互相等待,只有当多个线程都到达时,这些线程才会继续执行。

和 CountdownLatch 相似,都是通过维护计数器来实现的。线程执行 await() 方法之后计数器会减 1,并进行等待,直到计数器为 0,所有调用 await() 方法而在等待的线程才能继续执行。

CyclicBarrier 和 CountdownLatch 的一个区别是,CyclicBarrier 的计数器通过调用 reset() 方法可以循环使用,所以它才叫做循环屏障。

CyclicBarrier 有两个构造函数,其中 parties 指示计数器的初始值,barrierAction 在所有线程都到达屏障的时候会执行一次。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值