赞
踩
什么是AQS
实现AQS锁的简单概括
ReentrentLock的lock()方法执行流程(无其他线程争抢,成功获取锁)
ReentrentLock的lock()方法执行流程(无法获取锁)
依旧是刚才的测试案例,加了一个m3()方法,因为m1方法会重复调用1000次,每次停一秒,所以m3不会获取到锁
给m3打断点,开始debug
与上面的前几步一样,进到lock方法中
由于m1一直持有锁,所以m3自然修改不到state值,返回fasle,进入acquire(1)
里面调用了tryAcquire(arg),arg我们由上一步可以知道值为1,进入该方法
里面调用了nonfairTryAcquire(1),由名称我们可以推断ReentrantLock中锁的争抢是不公平的,继续进入
方法比较长,这里大概解释一下跳过几步:首先获得当前线程,然后获得此时state的值,如果state此时的值为0,代表现在没有线程持有锁,用CAS尝试获取锁,获取成功修改对应属性,返回true;若state不为0,判断当前线程是否为持有锁的线程(锁的重入),若是则state+1,返回true;但显然这里我们以上2种情况都不是,所以返回fasle!
回到第5步的地方,因为返回是false,所以!false为true,进入下面的addWaiter方法;若返回为true则不会进入!由方法名可以推断这是将当前线程放入等待队列的方法,传入的参数为一个标记,表示该节点正在独占模式下等待
我们知道AQS其实里面有一个等待队列,队列由Node组成,所以此时为创建一个Node,将当前线程给该Node,并将该Node放入等待队列
因为等待队列是双向链表,我们要往后添加需要获得当前最后一个Node,tail即为最后一个Node,如果tail不等于null,用CAS将该Node插入队列即可,但显然我们此时的tail为空,所以进入下面的enq(node)
进入enq发现里面是一个死循环,因为我们的tail为空(即等待队列为空),所以第一次进来,会进行CAS的初始化,发现初始化参数传入的是一个空Node,进入该方法
由注释我们可以知道这就是用CAS初始化等待队列的一个方法
回到之前的方法,由于是死循环且初始化完毕所以第二次我们就进到了else里,也是用CAS操作将Node放入队列尾,最后return,这里用死循环其实就是CAS的自旋,确保添加时的安全性
回到addWaiter,将node返回
下一步就是调用acquireQueued了,我们现在可以知道它的实参实际上是acquireQueued(node(当前线程的Node,处于等待队列尾),1),进入方法
这个方法会先获取当前node的上一个节点(node.predecessor()),如果上一个节点是首节点,则代表node可能会获取到锁,所以node尝试获取锁,成功则将node设置为首节点,返回false,acquire(第8步)结束;若再次获取失败,调用shouldParkAfterFailedAcquire方法,即将该线程阻塞,因为for是死循环,所以会一直进行上面的操作直到获取到锁!
总结:
参考的文章:https://blog.csdn.net/mulinsen77/article/details/84583716
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。