深剖「AQS(1)」

173 阅读2分钟

这是我参与8月更文挑战的第19天,活动详情查看:8月更文挑战

AQS

抽象的同步队列

  • 大家先来看类图

image.png

  • 当我们看到state第一个想法是否会想起上一篇文章「深入理解线程池」
  • 不错,AQS其实也在在维持一个变量为了呈现状态信息state
  • 其实但我们深入到读各个锁的源码就可以知道
  • ReentrantLock中的state是维护了当前线程获取锁的可重入次数
  • ReentrantReadWriteLock的state的高16位是读状态,就是获取读锁的状态,低16位是写状态,就是获取写锁的状态
  • semaphore是表示可用信号的个数

分类!

  • 其实我们要清晰的理解state,从这个状态值去入手源码
  • 我们就会发现,状态值分两种状态,分别是独占方式和共享方式
  • 在共享方式下,我们都能看到一个明显的词就是shared
  • 在共享方式下最重要的就是获取资源和释放资源也就是
  • acquireShared(int arg)``acquireSharedinterruptibly(int a)releaseShared(int arg)

独占方式

  • 独占顾名思义-就是如果一个线程获取到了资源,那么就会标记这个线程自己拿到了,其他线程再想改变state的时候,发现已经有人持有它了,那么就会失败进而堵塞
  • 回头来看独占锁ReentrantLock,它的实现就是获取锁以后,使用CAS在AQS内部将state的值改从0改为1
  • 改变值以后就将当前锁的持有人设置为当前线程,在此基础上还可以设置可冲入次数
  • 就是当前线程再次获取到这个锁🔐时,发现自己就是锁的持有法人,那么久将state从1设置成2
  • 上述的描述就是下面的代码
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;
}
  • 继续来看共享方式,其实它是与具体的线程无关的,因为在多个线程去请求资源的时候,
  • 其中一个获取到以后,其他的线程还是可以获取的,但是需要CAS的方式去获取

独占方式

  • 独占方式下当获取资源成功的时候便设置state的值再返回
  • 获取失败就多加了 步骤,是将当前的线程封装为Node节点,类型为Node.EXCLUSIVE,再插入到尾部
  • 调用LockSupport.park(this)挂起自己

image.png

独占-释放

  • 是通过尝试tryRelease操作,在一个线程调用release(int arg)的时候释放资源
  • 其中在上面啊哦做设置state的值,调用LockSupport.unpark(node.thread)在AQS红激活队列里面被阻塞的一个线程
  • 被激活的线程使用tryAcquire尝试,查看state是否满足,满足则被激活,否则继续在AQS队列中挂起

image.png

共享方式下就留给大家自己去看啦,思想与独占方式一样的