此篇文章不再AQS的基础理论和体系,会直接从AQS的源码和原理进行剖析,如果还不了解AQS的可以先看一下前两篇文章 AQS详解1-理论介绍 AQS详解2-体系架构介绍
一.先说原理结论.
在之前我们已经总结到AQS=state+CLH双端队列,利用CAS,自旋,以及LockSupport实现了对线程的等待唤醒操作. 这里以ReentrantLock以切入点,研究非公平锁的具体过程. 那究竟是怎样实现的呢?这里先说实现步骤. 1.加锁过程.
2.解锁过程.
二.使用源码验证.
???刚刚我大概画出了加锁和释放锁的流程,当然我画的可能是对的,也可能是错的,这些都需要看源码来验证. ???为了方便理解,这里引入一个简单的业务场景,有两个人a,b去银行办理业务,目前只有一个办理窗口,只能排队一个一个来,每个人大概需要二十分钟左右,业务代码很简单,大概如下所示. 所以现在的流程是这样的, 1.a先执行lock操作,可以抢到锁,执行业务逻辑; 2.b线程尝试获取锁失败,被阻塞;
(1) a先到了,准备抢锁.执行lock操作. 点进去lock方法,可以看到本质上还是执行的AQS中的lock方法. 再点进去,发现是一个抽象方法,所以必然是有子类实现具体逻辑,这里有两个子类,分别对应的是公平锁和非公平锁的实现,这里以非公平锁切入,所以点进NonfairSync. 点进来首先执行的compareAndSetState方法,也就是我之前所说的CAS操作了,看是否能执行成功,可以的话就说明获取到锁,然后将独占线程指向当前线程, 这里是a第一个获取锁的线程,当然是可以执行成功的,所以a获取到锁直接返回了. (2) a已经获取到锁,现在b执行加锁操作. 之前多余的步骤不再演示,这里b抢锁会失败,就会执行acquire方法. 点进来之后首先执行第一个方法tryAcquire,其实看方法名就可以见名知义,尝试获取锁, 点进来发现这个方法什么都每干,直接抛异常了,可能有些小伙伴直接蒙蔽了,这里其实就是典型的模板模式,指定一个方法,强制子类去实现,否则就会抛异常.所以这里选择子类的非公平锁方法去看 这个方法其实又是尝试获取锁的操作.但是不会获取成功,返回false. 执行完上述方法将会重新返回到acquire方法中,接着执行addWaiter方法. addWaiter方法主要就是将当前线程封装的节点加入到等待队列中 enq方法就是一个自旋操作,真正将当前线程节点加到队列中. 这里把代码中所做的操作用图来演示,可以看到,我们加入到队列中的第一个节点并不是当前线程封装的节点,而是一个空节点,为什么要这样做呢?这里留个悬念,后面会解答这个问题. 现在执行完addWaiter方法往上返,开始执行acquireQueued方法. 在acquireQueued()方法中会执行两次循环,第一次因为shouldParkAfterFailedAcquire()方法返回false而结束,第二次执行该方法的时候,因为WaitStatus状态已经更新为-1,就会直接返回true,从而可以执行parkAndCheckInterrupt()方法,在该方法中,将b线程进行阻塞.
至此,两个线程a和b抢锁操作已经完.线程a获取到锁,b线程进入阻塞状态.
因文章篇幅太长,这里分为两篇博客讲解,下一篇将继续讲解解锁的过程.还有小彩蛋. AQS详解3-源码解读(下)
|