AQS底层CLH同步队列的底层----源码解析

队列同步器AQS(AbstractQueuedSynchronizer)

源码解析

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
    //指向队列中的头节点
    private transient volatile Node head;
	//指向队列中的尾部节点
    private transient volatile Node tail;
	//当前状态
    private volatile int state;
    
    }
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;
        // 节点在等待队列中,线程在等待在Condition 上,其他线程对Condition调用singnal()方法后,该节点加入到同步队列中。 
        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() {    // 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;
        }
    }

从源码可以看出AQS的数据结构为:双向链表和Redis的链表结构类似
在这里插入图片描述
知道了底层的数据结构,我们来看实现的方法

入列

	//作用:构造节点以及加入到队列里面
	private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
       	//快速加入
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            //从CAS来设置加入到队列里面
            //这里node和tail进行了绑定
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
    	//通过死循环的方法来保证节点的正确添加
        enq(node);
        return node;
    }
	//基本步骤和上面方法相同不过是加了个死循环,只有添加成功才退出
	private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { 
                //如果队列为空的话,则必须主动创建一个队列
                //这里通过unsafe来新建对象
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                //这里加入队列的节点已经是队列中的第二个节点
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

compareAndSetHead(new Node()底层C++源码
● 通过obj+offset来寻找到要判断的对象
● 然后进行对象匹配,如果匹配的话,那么就把该地址的值改成新的

// Unsafe.h
virtual jboolean compareAndSwapObject(::java::lang::Object *, jlong, ::java::lang::Object *, ::java::lang::Object *);

// natUnsafe.cc
static inline bool
compareAndSwap (volatile jobject *addr, jobject old, jobject new_val)
{
	jboolean result = false;
	spinlock lock;
  
  	// 如果字段的地址与期望的地址相等则将字段的地址更新
	if ((result = (*addr == old)))
    	*addr = new_val;
	return result;
}

// natUnsafe.cc
jboolean
sun::misc::Unsafe::compareAndSwapObject (jobject obj, jlong offset,
                     jobject expect, jobject update)
{
	// 获取字段地址并转换为字符串
	jobject *addr = (jobject*)((char *) obj + offset);
	// 调用 compareAndSwap 方法进行比较
    return compareAndSwap (addr, expect, update);
}

● 当没有节点的时候,会调用compareAndSetHead(new Node())方法,在头节点的位置设置一个头节点并且尾节点也指向这个新节点
在这里插入图片描述

● 当头节点和尾节点存在的时候,开始添加新的节点到队列里面,也就是tail !=null

● 首先 新建一个node指向尾节点,要加入的节点指向原来的尾节点,并且用CAS让尾节点指向新节点,此时尾节点和Node节点处于双向绑定的状态,二者指向同一对象。

在这里插入图片描述

在这里插入图片描述

● 最后 将t的next节点(原来的尾节点)指向新节点。
在这里插入图片描述

● 当节点越来越多的时候就编变成来上述情况。
在这里插入图片描述

在这里插入图片描述

出列

	//将队列头设置为节点,从而退出队列。仅由acquire方法调用。为了GC和抑制不必要的信号和遍历,还将未使用的字段空出来。
	private void setHead(Node node) {
        head = node;
        node.thread = null;
        node.prev = null;
    }

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值