java的JUC包系列文章(一): AQS的初探文章---说一说 Node
程序员文章站
2024-02-02 08:27:10
...
本来准备写一篇AQS的完成解析,已经写了大部分了,但是觉得写得实在太乱了,而且很多细节的东西很难讲清楚,所以还是决定先写一些前文,都是属于AQS整体的一部分,也都是AQS的灵魂组件,所以单拎出来说一下。
本文先争取将贯穿AQS的一个组件先讲清楚。好了,直接进入本文的主题,AQS的内部类--Node
(一) Node是什么,为什么这么重要
Node是AQS中的一个静态的内部类,之所以说贯穿了AQS的整个实现是因为AQS是一个竞争锁的并发框架, 有竞争就会有失败,所以所有的失败的节点都会以Node的形式,等待在等待者队列中,知道放弃竞争了,或者竞争成功。
构建Node的时候都需要两个参数,一个是Thread,标识是哪个线程在等待,那当轮到你竞争的时候也会知道去唤醒哪个Thread进行工作,还有一个是Node.mode,这个是Node内部实现的标识当前Node是数据哪种竞争模式的,有两种不同的模式,独占模式(exclusive)和共享模式(Shared)
独占模式:资源是独占的,一次只能一个线程获取。
共享模式:同时可以被多个线程获取,具体的资源的个数可以通过参数指定。
直接看一下Node的代码
所以,根据定义我们可以画出AQS中等待者队列的模型
(二) Node的用处
在本系列的后面的文章中,我们会讲到,跟Node的模式分配一样,抢占资源也会有独占式和共享式的方法入口,AQS也留了供用户自定义的抢占逻辑方法。所以Node在整个框架中哪里用到呢?我们可以先自己想一下,如果自己设计一个最简单的抢占模型会怎么设计?肯定会有下面几个步骤
1: 提供一个资源供大家抢占,假设初始状态为1,被占用后设置为0.所以假设有多个线程去获取这个资源的时候,肯定会只有一个线程能抢占成功,那么其他的线程必须按序排好。同时也要考虑到多个节点往队伍中排会出现的并发问题
2: 当上一个节点消费完成后,要将资源释放出来,然后要通知排在队伍中的第一个节点,通知他可以开始竞争资源了。
在AQS框架中,大致的逻辑和我们想的差不多,而Node在其中扮演的就是作为在等待队伍中排队的节点的角色。然后节点中维护了前后节点的指针,所以可以在资源使用结束后通知排在后面的节点开始抢占。
本文先争取将贯穿AQS的一个组件先讲清楚。好了,直接进入本文的主题,AQS的内部类--Node
(一) Node是什么,为什么这么重要
Node是AQS中的一个静态的内部类,之所以说贯穿了AQS的整个实现是因为AQS是一个竞争锁的并发框架, 有竞争就会有失败,所以所有的失败的节点都会以Node的形式,等待在等待者队列中,知道放弃竞争了,或者竞争成功。
构建Node的时候都需要两个参数,一个是Thread,标识是哪个线程在等待,那当轮到你竞争的时候也会知道去唤醒哪个Thread进行工作,还有一个是Node.mode,这个是Node内部实现的标识当前Node是数据哪种竞争模式的,有两种不同的模式,独占模式(exclusive)和共享模式(Shared)
独占模式:资源是独占的,一次只能一个线程获取。
共享模式:同时可以被多个线程获取,具体的资源的个数可以通过参数指定。
直接看一下Node的代码
static final class Node { //标识为共享模式 static final Node SHARED = new Node(); //标识为独占模式 static final Node EXCLUSIVE = null; /** * 以下四个都是Node的状态 / // 标识当前Node取消了竞争 static final int CANCELLED = 1; // 表示Node的后继结点可以被唤醒了 static final int SIGNAL = -1; // 表示现在Node阻塞者等待某些条件满足 static final int CONDITION = -2; // #TODO static final int PROPAGATE = -3; volatile int waitStatus; // 在等待队列中的前一个节点 volatile Node prev; // 在等待队列中的后一个节点 volatile Node next; // 执行的线程 volatile Thread thread; Node nextWaiter; final boolean isShared() { return nextWaiter == SHARED; } Node() { // Used to establish initial head or SHARED marker } Node(Thread thread, Node mode) { // Used by addWaiter this.nextWaiter = mode; this.thread = thread; } Node(Thread thread, int waitStatus) { // Used by Condition this.waitStatus = waitStatus; this.thread = thread; } } // 等待队列中的首节点 private transient volatile Node tail; // 等待队列中的尾节点 private transient volatile Node head;
所以,根据定义我们可以画出AQS中等待者队列的模型
(二) Node的用处
在本系列的后面的文章中,我们会讲到,跟Node的模式分配一样,抢占资源也会有独占式和共享式的方法入口,AQS也留了供用户自定义的抢占逻辑方法。所以Node在整个框架中哪里用到呢?我们可以先自己想一下,如果自己设计一个最简单的抢占模型会怎么设计?肯定会有下面几个步骤
1: 提供一个资源供大家抢占,假设初始状态为1,被占用后设置为0.所以假设有多个线程去获取这个资源的时候,肯定会只有一个线程能抢占成功,那么其他的线程必须按序排好。同时也要考虑到多个节点往队伍中排会出现的并发问题
2: 当上一个节点消费完成后,要将资源释放出来,然后要通知排在队伍中的第一个节点,通知他可以开始竞争资源了。
在AQS框架中,大致的逻辑和我们想的差不多,而Node在其中扮演的就是作为在等待队伍中排队的节点的角色。然后节点中维护了前后节点的指针,所以可以在资源使用结束后通知排在后面的节点开始抢占。
上一篇: python文件路径操作