AQS源码分析讲解
首先来一幅AQS整体数据结构源码
AQS中有很多重要的成员变量其中包括上面图中的Head,Tail。它们永远保存当前被阻塞住的线程按照顺序依次排列下去
上面Node中的成员变量Thread就是存放阻塞的线程变量
有了上面大概的信息我们开始进入源码分析,
1.AQS是如何让线程阻塞的了?
上面acquier方法中tryAcquire是一个抽象方法。AQS采用模板方法设计模式让其子类实现。
addWaiter是将当前的运行的线程封装Node节点对象,并插入到等待队列中
acquireQueued则是判断当前添加的节点是否能够运行,是否需要将当前线程阻塞挂起
先看下addWaiter的源码实现
enq是将第一步不能插入到等待队列中的node继续进行插入直到成功为止
acquireQueued中设计将当前线程挂起逻辑具体如下
我们先看parkAndCheckInterrupt的源码最终进入下面
上面就是调用Unsafe上面的park方法将当前线程挂起
上面会修改pre的waitStatus为SIGNAL在acquire方法中是采用for(;;)不断调用马上又会再次进入到上面方法由于上次将pre中的waitStatus修改为SIGNAL所有马上会返回true。接着会进入到parkAndCheckInterrupt方法中将当前线程挂起
如果被唤醒后会再次判断当前节点的头是否为head,如果是head,会再次试图获取state。如果成功会将头设置为head
2.挂起中等待列表中的线程是如何被唤醒的了?
在上面我们知道线程会被unsafe调用park进行挂起,那么一定存在某个方法嗲用unsafe的unpark操作唤醒线程
上面就是唤醒等待列表中线程的方法
tryRelease也是一个模板方法待子类去实现
unparkSuccessor就是最核心的方法