AQS

一、为什么需要AQS

仔细观察锁和协作类,就会发现有一个共同点:闸门

既然这些协作类,它们有很多工作都是类似的,所以如果提取出一个工具类,那么就可以直接用。对于ReentrantLock和Semaphore的实现而言,就可以屏蔽很多细节,只关注自己的业务逻辑就可以了。

1.如果没有AQS

就需要每个工具类自己实现:

  • 同步状态的原子性管理
  • 线程的阻塞与解除阻塞
  • 队列的管理

2.Semaphore和AQS的关系

Semaphore内部有一个Sync类,其继承了AQS。

同样的,CountDownLatch中也有一个Sync内部类。

3.AQS的作用

AQS是一个用于构建锁、同步器、协作工具类的工具类(框架)。有了AQS以后,更多的协作工具类都可以很方便的被写出来。

二、内部原理解析

1.核心三大部分

(1)state状态

state具体的含义根据具体实现类的不同而不同。比如CountDownLatch中表示“还需倒数数量”,Semaphore中表示“剩余许可证数量”。

state由volatile修饰,所有修改state的方法都需要保证线程安全。

(2)控制线程抢锁和配合的FIFO队列

用来存放等待的线程
在这里插入图片描述

(3)期望协作工具类实现的 获取/释放 等重要方法

这个方法需要协作类自己实现,且含义各不相同。

  • 获取操作会依赖state变量,经常会阻塞;
  • 释放操作不会阻塞;

2.源码解析

(1)AQS用法
  1. 写一个类,想好协作的逻辑,实现获取/释放方法
  2. 内部写一个Sync类继承AbstractQueuedSynchronizer
  3. 独占则重写tryAcquire/tryRelease。共享则重写tryAcquireShared(int acquires)和tryReleaseShared(int releases)等方法。在之前写的获取/释放方法中调用AQS的acquire/release或者Shared方法。
(2)CountDownLatch源码分析
构造方法
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

构造方法中根据传入的count构造Sync实例。

        Sync(int count) {
            setState(count);
        }
getCount()
    public long getCount() {
        return sync.getCount();
    }
		//sync中的
        int getCount() {
            return getState();
        }
await()
    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

如果tryAcquireShared(arg) < 0,也就是没有获取到锁,那么就要入队并且使其阻塞。如果>0说明正常获取到锁,可以继续执行。
CountDownLatch中是这样实现tryAcquireShared的:

        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }
countDown()
    public void countDown() {
        sync.releaseShared(1);
    }
    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

tryReleaseShared同样是在CountDownLatch中实现的:

        protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
    }

可以看到,在release的时候是不用阻塞线程的,它是利用CAS来实现state值的-1的。
如果此时state的值为0,那么就返回true。当返回true时,执行doReleaseShared();,会唤醒所有线程。

(3)Semaphore源码分析
aquire
    public void acquire(int permits) throws InterruptedException {
        if (permits < 0) throw new IllegalArgumentException();
        sync.acquireSharedInterruptibly(permits);
    }

可以看到,和CountDownLatch的await()方法是相似的,区别在于Semaphore这里可以获取指定数量。

tryAcquireShared

有公平、非公平两种实现:

nonfairTryAcquireShared(int acquires)
        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

如果小于0,说明许可证不够用,那么在调用它的acquireSharedInterruptibly中就会把这个线程入队并阻塞。
大于0意味着成功拿到许可证。

tryAcquireShared
        protected int tryAcquireShared(int acquires) {
            for (;;) {
                if (hasQueuedPredecessors())
                    return -1;
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

和非公平的相比,有了一个新的判断条件,这个判断条件就是判断是否是队首。hasQueuedPredecessors()为true意味着不是队首,直接返回-1。为false说明是队首,则可以尝试获取许可证。

(4)ReentrantLock源码分析
非公平锁的lock()
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

如果锁没有持有者,则设置此线程为持有者;否则aquire()。

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

首先将传入的参数,传入到tryAcquire方法中,这个方法在非公平锁中的实现为:

        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

其逻辑为首先判断锁是否有持有者,没有则将当前线程作为持有者;
有的话判断当前线程是不是就是持有者,是的话重入acquires次;
都不是返回false。

unLock()
    public void unlock() {
        sync.release(1);
    }

调用release()方法:

    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

首先会执行tryRelease(arg)进行尝试释放锁:

        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

如果当前线程不是锁的持有者,返回false;
如果state减去releases后值为0,说明这把锁已经没有人持有了,此时将锁的持有者设为null,state设置为0,返回true。

此时在release中倘若返回结果为true,则唤醒所有被阻塞的线程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值