关于AQS中的enq方法的理解

  • Post author:
  • Post category:其他


自己太笨了,总感觉有点绕,就整理下吧~

private Node enq(final Node node) {
	//自旋锁
    for (;;) {
        //tail默认就是null
        Node t = tail;
        if (t == null) { // Must initialize
            //因为tail默认是null,所以首次一定会进来
            //compareAndSetHead在下面
            //也就是首次一定会把一个新的node设置为head
            if (compareAndSetHead(new Node()))
                //tail=head=new Node()
                tail = head;
            //到这里就是tail和head都会指向new Node
        } else {
            //第二次一定进入else发
            //假如此时进入了一个线程A,这个A已经被封装成了node传进来
            //当前的node的pre指向t,也就是tail,也就是刚才创建的Node,
            //因为第一行就定义了Node t = tail,而t=head=node
            node.prev = t;
            //这里看下面的compareAndSetTail方法
            //把tail节点赋值为新传入的node(Thread A),赋值操作就相当于指向
            if (compareAndSetTail(t, node)) {
                //这里的t指的是原来的tail节点,tail指向一开始的new Node
                //所以就是new Node的next指向新传入的node(Thread A)
                t.next = node;
                return t;
            }
        }
    }
}


private final boolean compareAndSetHead(Node update) {
    //当前的head字段,和null值比对,默认是null,所以相等,所以赋值为update,也就是new node()
    return unsafe.compareAndSwapObject(this, headOffset, null, update);
}


private final boolean compareAndSetTail(Node expect, Node update) {
    //当前的tail字段和期望值exepct,即t进行比较,一定是相等的啊,以为t=tail么,所以更新赋值为update,
    //即新传进来的node(Thread A)
    return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}

第一次一定是进入if

在这里插入图片描述

当有新的线程进入的时候,进入else(其实没有新的线程进入还是要进入else的),红色的是else所做的操作

在这里插入图片描述

总结:

这里面head和tail都是一个node,但是这里我理解为一个类似于指针的东西,它们存在的意义也是为了维持这个双向的链表,没有实际的意义,包括prev和next也是。

还是挺绕的,感觉没必要理解这么透彻,主要思想就是

第一次进来的时候创建这个双向链表的空节点,后面传入新的节点的时候,开始往双向链表后面添加新的节点,而head和tail永远指向第一个和最后一个,中间都是一些切换指针(我这里暂且理解为指针吧,也就是赋值操作,比如tail和head的赋值,prev和next的赋值。)

for(;;)是一个自旋锁,为了不停的去尝试添加节点,但是可能存在并发问题,所以通过cas的方式,添加,可能上次被线程a抢占先机,这次自己还是要去尝试,如何进行不断的尝试?所以通过自旋锁解决,当然如果添加成果最后会通过return跳出自旋锁。

个人认为这个自旋锁和cas的操作用的很厉害,值得学习!

仔细想想为什么这么设计?

因为当前的这个compareAndSetTail和compareAndSetHead,这两个操作,简单说就是为了切换指针指向,简单说就是一“操作”,任何操作,因为在操作系统中,虽然我们目前在解决并发问题,但是也是存在并发问题的,对于这个“操作来说”。所以我们可以选择加锁,也可以选择这种cas自旋锁的方式,因为这个操作可能是很快的,因为可能同时又一万个线程都被阻塞了(极端一点的情况),这个时候,会有很多的切换指针的这个“操作”,反复的加悲观锁的话,没有cas这种乐观锁好。简单总结下,就是这种cas自旋锁的方式能提升性能。

以上全是我的个人理解,可能存在很大的问题,希望没有误导到你,如果有问题还希望您及时指出。



版权声明:本文为dataiyangu原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。