关于juc:jucAQS源码分析1

9次阅读

共计 2663 个字符,预计需要花费 7 分钟才能阅读完成。

1. AQS 类了解

AQS 如果作为一个幼儿园老师来看,她的小名叫 ” 锁 ” 老师,手里拿一个 ”state” 的玩具按钮,负责有保护孩子们玩玩具 ” 先来后到 ” 的秩序。如果把其余小孩叫 ” 线程 ” 小朋友,当他们一起玩儿滑滑梯时,每次只能上一个。此时就须要 ” 锁 ” 老师来守在滑滑梯口儿上:小 ” 线程 ” 们奔过去,谁先把 ” 锁 ” 老师手里的按钮 state 按亮了,谁就先上; 按不亮的,就让 ” 锁 ” 老师来给你前胸后背都给你贴一个挂钩,挂着后面比你先上的小朋友和你前面紧跟着的小朋友。

另外如果有某些 ” 线程 ” 小朋友要喝水,” 锁 ” 老师也给这些小朋友每个人头上戴一个帽子,帽子上写着下一个喝水的 ” 线程 ” 小朋友的名字。这样喝水也有程序了。

下面的玩儿滑滑梯的程序,前后的钩子,就是 AQS 里外部类 Node 里的 prev/next 援用,保护 ” 阻塞队列 ” 的 FIFO 程序;它是双向的链表。

下面等喝水的小朋友帽子上的名字,就是 Node 里的 nextWaiter 援用,保护 ” 条件队列 ” 的程序,它是单向的链表。

AQS 类图:

2. AQS 两个队列: 阻塞队列和条件队列

2.1 阻塞队列

2.1.1 双向链表实现的 FIFO 队列

一个 CLH 队列,他是 FIFO 的,实现形式为双向链表的构造,具体构造定义见 AQS 的外部动态类 Node,如果要偏心实现,就是先来后到的。

2.1.2 双向链表构造 Node

FIFO 队列内节点的数据结构:外部类:Node

2.2 条件队列

2.2.1 单向链表的条件队列:见 Node 的 nextWaiter

阻塞队列是 FIFO 的,是双向链表,在 Node 链对象里是 prev+next
条件队列是单向,只存储下一个期待者的援用,在 Node 里是 nextWaiter;
应用形式如上所示

2.2.2 条件队列的条件构造:AQS>ConditionObject

条件队列的实现,也是在 AQS 中,体现在 ReentrantLock 里就是 lock.newCondition()的 Condition; sync 自身就是 AQS 的子类. 看上面的源码:ReentrantLock 的 newCondition 应用的是本人外部的 Sync 的 newCondition(),而 sync 就是 AQS,sync.newCondition()生成的就是 ConditionObject。也就是 AQS 外部类的 ConditionObject。

// ReentrantLock#newCondition
public Condition newCondition() {return sync.newCondition();
}
// ReentrantLock.Sync#newCondition
final ConditionObject newCondition() {return new ConditionObject();
}

2.3 一个实例

条件队列和可重入锁示例:

@Test
public void testConditional() throws InterruptedException {
    // AQS 锁
    ReentrantLock lock = new ReentrantLock();
    // AQS 条件
    Condition condition = lock.newCondition();

    Thread t1 = new Thread(() -> {System.out.println("======================t1.print==============");
        lock.lock();

        try {System.out.println("t1 await start ....");
            condition.await();
            System.out.println("t1 await end");
        } catch (InterruptedException e) {e.printStackTrace();
        } finally {lock.unlock();
        }

    });

    Thread t3 = new Thread(() -> {System.out.println("======================t3.print==============");
        lock.lock();

        try {System.out.println("t3 await start ....");
            condition.await();
            System.out.println("t3 await end");
        } catch (InterruptedException e) {e.printStackTrace();
        } finally {lock.unlock();
        }

    });

    Thread t2 = new Thread(() -> {System.out.println("======================t2.print==============");
        lock.lock();
        try {System.out.println("signal start ....");
            condition.signalAll();
            System.out.println("signal end");
        } finally {lock.unlock();
        }

    });

    t1.start();
    t3.start();
    Thread.sleep(400);
    t2.start();}

输入后果如下:

======================t1.print==============
t1 await start ....
======================t3.print==============
t3 await start ....
======================t2.print==============
signal start ....
signal end
t1 await end
t3 await end

3.AQS 的特色

3.1 抽象类

指明 AQS 是抽象类,所以它 ” 有一些办法被作为模板模式被后辈应用 ”

3.2 一个 state 状态

state 是 int 的,volatile 的,是一个开关,敞开 (state==0) 示意 AQS 锁没被人占用;
state== 1 示意 AQS 锁被人占用了;
state 大于 1 示意正以重入锁形式被以后独占线程占用。

3.3 父类还有个独占锁线程

Thread exclusiveOwnerThread 持有独占锁的线程。

3.4 ReentrantLock 类中的 AQS 子类

ReentrantLock 中的 Sync 外部类继承了 AQS,而后别离实现了两个锁:FairSync 是偏心锁;
NonFairSync 是非偏心锁;具体的实现,前面会具体记录一篇 ReentrantLock。

正文完
 0