java深入理解AQS

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/eve8888888/article/details/84594769

一、AQS简介

在同步组件的实现中,AQS是核心部分,同步组件的实现者通过使用AQS提供的模板方法实现同步组件语义,AQS则实现了对同步状态的管理,以及对阻塞线程进行排队,等待通知等等一些底层的实现处理。也包括了独占锁的获取与释放,共享锁的获取与释放,以及超时获取锁,中断获取锁等特性的实现。

比如说有关独占锁的一些模板方法:

1. void acquire(int arg) : 独占式获取同步状态,如果获取失败则插入同步队列进行等待。
2. void acquireInterruptibly(int arg) : 与acquire方法相同,但在同步队列中等待时可以响应中断。
3. boolean tryAcquire(int arg) : 获取锁成功返回true,否则返回false
4. boolean tryAcquireNanos(int arg,long nanosTimeout) : 在3的基础上增加了超时等待功能,
在超时时间内没有获得同步状态返回false
5. boolean release(int arg) : 释放同步状态,该方法会唤醒在同步队列中的下一个节点。

要想了解AQS的底层实现,还要了解什么是同步队列

二、同步队列

当共享资源被某个线程占有,其他请求该资源的线程将会阻塞,从而进入同步队列。就数据结构而言,队列的实现方式无外乎两者一是通过数组的形式,另外一种则是链表的形式。而AQS的同步队列时用那种方式实现的呢?我们可以看一下AQS的源码

AQS的全称是AbstractQueuedSynchronizer

可以看到AQS有一个静态内部类叫Node,并且有Node类型的prev和next变量,写过链表的肯定会熟悉,prev是前驱结点,next是后继结点,从源码很容易看出这是一个双链表。其中还定义了结点状态volatile int waitStatus、当前结点所包装的现场对象volatile Thread thread以及等待队列的下一个结点 Node nextWaiter。

结点状态

1. int INITIAL = 0; // 初始状态
2. int CANCELLED = 1; // 当前节点从同步队列中取消
3. int SIGNAL = -1; // 后继节点的线程处于等待状态,如果当前节点释放同步状态会通知后继节点,使得后继节点的线程继续运行。
4. int CONDITION = -2; // 节点在等待队列中,节点线程等待在Condition上,当其他线程对Condition调用了signal()方法后,该节点将会从等待队列中转移到同步队列中,加入到对同步状态的获取中。
5. int PROPAGATE = -3; // 表示下一次共享式同步状态获取将会无条件地被传播下去。

写一段简单的代码看一下

public class Test {
    private static Lock lock = new ReentrantLock();
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new Thread(()->{
                lock.lock();
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    lock.unlock();
                }
            }).start();
        }
    }
}

Debug一下

可以清楚的看到每个结点有两个域:prev和next,其实AQS还有两个重要的成员变量head和tail,可以看出head和tail管理了同步队列中的结点,也就是说AQS实际上通过头尾指针来管理同步队列,同时实现包括获取锁失败的线程进行入队,释放锁时对同步队列中的线程进行通知等核心方法。示意图如下:

展开阅读全文

没有更多推荐了,返回首页