栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

浅谈Java并发 J.U.C之AQS:CLH同步队列

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

浅谈Java并发 J.U.C之AQS:CLH同步队列

CLH同步队列是一个FIFO双向队列,AQS依赖它来完成同步状态的管理,当前线程如果获取同步状态失败时,AQS则会将当前线程已经等待状态等信息构造成一个节点(Node)并将其加入到CLH同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点唤醒(公平锁),使其再次尝试获取同步状态。

在CLH同步队列中,一个节点表示一个线程,它保存着线程的引用(thread)、状态(waitStatus)、前驱节点(prev)、后继节点(next),其定义如下:

static final class Node {
 
 static final Node SHARED = new Node();

 
 static final Node EXCLUSIVE = null;

 
 static final int CANCELLED = 1;

 
 static final int SIGNAL = -1;

 
 static final int ConDITION = -2;

 
 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;
 }

 final Node predecessor() throws NullPointerException {
 Node p = prev;
 if (p == null)
 throw new NullPointerException();
 else
 return p;
 }

 Node() {
 }

 Node(Thread thread, Node mode) {
 this.nextWaiter = mode;
 this.thread = thread;
 }

 Node(Thread thread, int waitStatus) {
 this.waitStatus = waitStatus;
 this.thread = thread;
 }
}

CLH同步队列结构图如下:

入列

学了数据结构的我们,CLH队列入列是再简单不过了,无非就是tail指向新节点、新节点的prev指向当前最后的节点,当前最后一个节点的next指向当前节点。代码我们可以看看addWaiter(Node node)方法:

 private Node addWaiter(Node mode) {
 //新建Node
 Node node = new Node(Thread.currentThread(), mode);
 //快速尝试添加尾节点
 Node pred = tail;
 if (pred != null) {
 node.prev = pred;
 //CAS设置尾节点
 if (compareAndSetTail(pred, node)) {
 pred.next = node;
 return node;
 }
 }
 //多次尝试
 enq(node);
 return node;
 }

addWaiter(Node node)先通过快速尝试设置尾节点,如果失败,则调用enq(Node node)方法设置尾节点

 private Node enq(final Node node) {
 //多次尝试,直到成功为止
 for (;;) {
 Node t = tail;
 //tail不存在,设置为首节点
 if (t == null) {
 if (compareAndSetHead(new Node()))
 tail = head;
 } else {
 //设置为尾节点
 node.prev = t;
 if (compareAndSetTail(t, node)) {
 t.next = node;
 return t;
 }
 }
 }
 }

在上面代码中,两个方法都是通过一个CAS方法compareAndSetTail(Node expect, Node update)来设置尾节点,该方法可以确保节点是线程安全添加的。在enq(Node node)方法中,AQS通过“死循环”的方式来保证节点可以正确添加,只有成功添加后,当前线程才会从该方法返回,否则会一直执行下去。

过程图如下:

出列

CLH同步队列遵循FIFO,首节点的线程释放同步状态后,将会唤醒它的后继节点(next),而后继节点将会在获取同步状态成功时将自己设置为首节点,这个过程非常简单,head执行该节点并断开原首节点的next和当前节点的prev即可,注意在这个过程是不需要使用CAS来保证的,因为只有一个线程能够成功获取到同步状态。过程图如下:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持考高分网

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/138209.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号