AQS的使用和reentrantlock

本文深入解析了AQS(AbstractQueuedSynchronizer)的使用方式与ReentrantLock的实现原理,涵盖AQS的基本概念、独占与共享模式的实现细节,以及如何通过AQS构建自定义锁。同时,探讨了ReentrantLock如何利用AQS实现可重入锁与条件变量,为并发编程提供深入理解。

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

AQS的使用和reentrantlock

AQS概述

AQS是一个同步器,全称是AbstractQueuedSynchronizer类。

使用方法:子类继承AQS,然后重写tryAcquire、tryRelease、isHeldExclusively(如果是共享模式实现tryAcquireShared和tryReleaseShared方法),然后将其作为内部类,外部类定义unlock和lock方法并调用acquire和release方法即可(共享模式调用acquireShared和releaseShared)。

线程协调场景主要分为两类,一个线程在进行操作时不允许其他线程操作,即独占模式,还有允许多个线程操作的情况,称为共享模式。(独占和共享两种模式可以两个同时实现)

一个关键变量:内部封装了一个volatile修饰的int类型的字段state,代表当前同步状态。

定义了访问该字段的几个方法getState()、setState(val)、compareAndSetState(expect, update)

独占模式

独占模式下保证线程同步的操作是这样的,将state设为0,当某个线程要独占时都需要先判断state是不是0,如果不是就进入阻塞状态等待,如果是0就把state设置为1。然后进行操作,实现这个操作需要设置3种操作:分别是尝试获取同步状态、释放同步状态、判断是否有线程独占。

这三种操作分别对应下面三个方法tryAcquire(tryAcquireNanos方法加了超时限制,超时就自动视为获取失败)、tryRelease、isHeldExclusively。

public class PlainLock {

    private static class Sync extends AbstractQueuedSynchronizer{

        @Override
        protected boolean tryAcquire(int arg) {
            // TODO Auto-generated method stub
            return compareAndSetState(0, 1);
        }

        @Override
        protected boolean tryRelease(int arg) {
            // TODO Auto-generated method stub
            setState(0);
            return true;
        }

        @Override
        protected boolean isHeldExclusively() {
            // TODO Auto-generated method stub
            return getState() == 1;
        }
    }
    
    private Sync sync = new Sync();
    public void lock() {
        sync.acquire(1);
    }
    
    public void unlock() {
        sync.release(1);
    }
}

共享模式

当同步器使用共享模式时,区别在于允许多个线程持有资源,这个数量就是state的初始值,共享模式下需要定义两个方法tryAcquireShared(val)、tryReleaseShared(val),也是尝试获得和释放同步状态,尝试获得时返回值为int数,返回一个非负数代表获得同步状态成功(需要在aqs的构造方法中设置setState方法来确定可以同时并发的线程),然后重定义lock和unlock方法内部使用acquireShared和releaseShared方法。

//保证不超过两个线程同时访问的锁
public class DoubleLock {

    private class Sync extends AbstractQueuedSynchronizer{
        
        public Sync() {
            super();
            setState(2);
        }
        
        //共享模式下需要一开始在构造方法里设置state的值
        //然后在重写tryAcquireShared和tryReleaseShared方法
        
        //该返回值大于等于0说明获取同步状态成功,否则加入同步队列
        @Override
        protected int tryAcquireShared(int arg) {
            // TODO Auto-generated method stub
            while(true) {
                int now = getState();
                int next = getState() - arg;
                if(compareAndSetState(now, next)) {
                    return next;
                }
            }
        }

        @Override
        protected boolean tryReleaseShared(int arg) {
            // TODO Auto-generated method stub
            while(true) {
                int now = getState();
                int next = getState() + arg;
                if(compareAndSetState(now, next)) {
                    return true;
                }
            }
        }
    }
    
    private Sync sy = new Sync();
    
    public void lock() {
        sy.acquireShared(1);
    }
    
    public void unlock() {
        sy.releaseShared(1);
    }   
}

acquire和release方法

AQS内部维护了一个node类,node中有两个指针和一个线程Thread,AQS内部维护了两个node,分别是node队列的头和尾节点。

acquire(args)方法就是获取同步状态,如果成功就返回,如果失败就将本线程加入同步队列。内部首先调用tryAcquire(args)方法尝试获取同步状态,如果失败之后会调用addWaiter和acquireQueued方法封装node插入同步队列并再次尝试获取,失败后会检查队列中node的状态,修改node的状态并阻塞。

(还可以使用acquireInterruptibly方法,它是可中断的,如果没获取到而阻塞,其他线程中断了该线程就会抛出interruptedEx异常)

release(args)方法就是释放同步状态,会调用tryrelease方法,如果成功了就唤醒同步队列的下一个节点,并释放头节点。

reentrantlock的实现

Reentrantlock内部定义了一个AQS的子类,每次new一个Reentrantlock就相当于new了一个AQS,内部封装着一个同步队列,AQS内部还有一个ConditionObject的内部类,这个内部类内部维护了两个node类型变量,相当于维护了一个队列,AQS中的队列和这个ConditionObject中的队列用的是同一个node。

Reentrantlock有一个方法newCondition,这个方法会返回一个ConditionObject类,也就是说每调用newCondition方法一次,就会构建出一个队列(这个等待队列是单向链表,而aqs同步队列是双向链表)。

ConditionObject类实现了condition接口,这个接口内部有await和signal方法,await方法执行后,该线程对应的node就会被放入对应condition的队列中,从AQS同步队列中取下(当取下node后AQS同步队列为空时那么最后这一个node就不会被取下),同时改变node的等待状态,signal方法负责唤醒线程,就是将对应condition中的node取下再放回AQS同步队列中去,同时改变node的等待状态。

Reentrantlock这种可以创建多个condition,也就是创建了多个等待队列,可以实现特定的唤醒,使一个显式锁对应多个等待队列的效果。

转载于:https://www.cnblogs.com/shizhuoping/p/11532096.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值