JUC -- AQS 原理

76 阅读4分钟

一、AQS 原理

概述

全称是 AbstractQueuedSynchronizer,是阻塞式锁和相关的同步器工具的框架

AbstractQueuedSynchronizer有一个静态内部类Node,属性有

volatile Thread thread;

volatile Node prev;

volatile Node next;

volatile int waitStatus;

Node nextWaiter;

AbstractQueuedSynchronizer的重要属性有

private volatile int state;

private transient volatile Node head;

private transient volatile Node tail;

特点:

state属性 独占/共享模式

  • AQS通过一个整数state变量来表示同步状态,不同的同步器可以根据自己的需求定义state的含义,例如对于独占锁,state可以表示锁的持有状态(0未持有锁,1持有锁);对于共享锁,state可以表示可用资源的数量

    • getState - 获取 state 状态
    • setState - 设置 state 状态
    • compareAndSetState - cas 机制设置 state 状态
    • 独占模式是只有一个线程能够访问资源,而共享模式可以允许多个线程访问资源
    • state 内部使用 volatile 配合 cas 保证其修改时的原子性

等待队列

  • 提供了基于 FIFO(先进先出) 的等待队列,管理被阻塞的线程。当线程获取同步状态失败时,会被加入等待队列,其他线程释放同步状态被唤醒。
  • AQS会将线程包装为Node节点,将其插入同步队列尾部,在资源可用时,队列头部节点会尝试再次获取资源。

条件变量

  • 条件变量来实现等待、唤醒机制,支持多个条件变量。

需要子类实现的方法

AQS 是基于模板方法模式的,AQS 是个抽象类,使用者需要继承 AQS 并重写指定方法。

以下这些方式是没有具体实现的,需要在使用 AQS 时在子类中去实现具体方法:

  • protected boolean tryAcquire (int arg) 独占式获取同步状态,实现该方法须查询并判断当前状态是否符合预期,然后再进行CAS设置状态。

  • protected boolean tryRelease (int arg) 独占式释放同步状态,等待获取同步状态的线程将有机会获取同步状态。

  • protected int tryAcquireShared (int arg) 共享式获取同步状态,返回大于0的值表示获取成功,反之获取失败。

  • protected boolean tryReleaseShared (int arg) 共享式释放同步状态。

  • protected boolean isHeldExclusively () 当前同步器是否再独占模式下被线程占用,一般用来表示是否被当前线程独占。

获取锁的姿势

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

释放锁的姿势

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

二、实现不可重入锁

自定义同步器、不可重入锁

在自定义同步器MySync类中重写tryAcquire等方法,在MyLock自定义锁类中调用acquire来获取锁时,底层也会调用tryAcquire方法进行判断,是否锁同理。

class MyLock implements Lock { //自定义锁(不可重入锁)

    class MySync extends AbstractQueuedSynchronizer { //自定义同步器, 不可重入锁 == 独占锁

        @Override
        protected boolean tryAcquire(int arg) {
            if(arg == 1) {
                //采用CAS,将0变为1来判断多线程下自己是否获得了锁
                if(compareAndSetState(0, 1)) {
                    //加锁,将持有锁的线程设为当前线程
                    setExclusiveOwnerThread(Thread.currentThread());
                    return true;
                }
            }
            return false;
        }

        @Override
        protected boolean tryRelease(int arg) {
            if(arg == 1) {
                if(getState() == 0) {
                    throw new IllegalMonitorStateException();
                }
                setExclusiveOwnerThread(null);
                setState(0);
                return true;
            }
            return false;
        }

        @Override //是否持有独占锁
        protected boolean isHeldExclusively() {
            return getState() == 1;
        }

        public Condition newCondition() { //AQS内部提供ConditionObject来创建条件变量对象
            return new ConditionObject();
        }
    }

    private MySync mySync = new MySync();

    @Override //加锁 (不成功,进入等待队列等待)
    public void lock() {
        mySync.acquire(1); //底层调用重写的tryAcquire,释放同理
    }

    @Override //加锁,可打断
    public void lockInterruptibly() throws InterruptedException {
        mySync.acquireInterruptibly(1);
    }

    @Override //尝试加锁(一次)
    public boolean tryLock() {
        return mySync.tryAcquire(1);
    }

    @Override //参数time和unit都是转换时间的,与tryAcquireNanos第一个参数无关
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return mySync.tryAcquireNanos(1, unit.toNanos(time));
    }

    @Override //释放锁
    public void unlock() {
        mySync.release(1);
    }

    @Override //创建条件变量对象
    public Condition newCondition() {
        return mySync.newCondition();
    }
}

测试

MyLock lock = new MyLock();

new Thread(() -> {
    lock.lock();
    try {
        log.error("locking...");
        sleep(1000L);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    } finally {
        log.error("unlocking...");
        lock.unlock();
    }
},"t1").start();

new Thread(() -> {
    lock.lock();
    try {
        log.error("locking...");
    } finally {
        log.error("unlocking...");
        lock.unlock();
    }
},"t2").start();

输出:

17:52:07.699 [t1] ERROR com.ssm.user.juc.TestPool - locking...
17:52:08.720 [t1] ERROR com.ssm.user.juc.TestPool - unlocking...
17:52:08.721 [t2] ERROR com.ssm.user.juc.TestPool - locking...
17:52:08.721 [t2] ERROR com.ssm.user.juc.TestPool - unlocking...