刚刚看完了并发实践这本书,算是理论具备了,看到了AQS的介绍,再看看源码,发现要想把并发理解透还是很难得,花了几个小时细分析了一下把可能出现的场景尽可能的往代码中去套,还是有些收获,但是真的很费脑,还是对多线程的理解太浅了,不多说了,直接上代码吧。这段代码不是为跑通,只是把AQS ...
刚刚看完了并发实践这本书,算是理论具备了,看到了AQS的介绍,再看看源码,发现要想把并发理解透还是很难得,花了几个小时细分析了一下把可能出现的场景尽可能的往代码中去套,还是有些收获,但是真的很费脑,还是对多线程的理解太浅了,不多说了,直接上代码吧。
这段代码不是为跑通,只是把AQS,ReentrantLock中的部分源码合并到了一起,便于理解。
1 package com.yb.interview.concurrent; 2 3 4 import java.util.concurrent.locks.LockSupport; 5 6 public class AQSSourceStudy { 7 8 abstract static class AQS { 9 /** 10 * 这个状态是有子类来维护的,AQS不会用这个状态做什么 11 */ 12 private volatile int state; 13 /** 14 * 队尾节点 15 */ 16 private volatile Node tail; 17 /** 18 * 可能情况 19 */ 20 private volatile Node head; 21 /** 22 * 独占线程 23 */ 24 private Thread exclusiveOwnerThread; 25 26 27 /** 28 * 由子类实现 29 * 判断当前线程是否需要排队 30 */ 31 abstract boolean tryAcquire(int i); 32 33 public int getState() { 34 return state; 35 } 36 37 public void setState(int state) { 38 this.state = state; 39 } 40 41 /** 42 * 主方法 43 * 可能的情况 44 * 当前状态可以直接运行 45 * 当前状态要放入队列里等待 46 * 状态->子类获取 47 * 过程,尽可能的不要去阻塞,循环多次,竞争多次 48 * 创建节点 49 * 节点入队,队尾 50 * 判断新节点的前一个节点的状态,更新,前一个节点,因为在入队的过程中每个节点的状态是动的 51 * 最后,阻塞当前线程 52 */ 53 public final void acquire(int arg) { 54 if (!tryAcquire(arg) && 55 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) 56 // 中断状态传播 57 // 实时或者将来阻塞,抛中断异常 58 selfInterrupt(); 59 } 60 61 /** 62 * 当有新节点入队时,循环的把新节点关联到一个有效节点的后面 63 * 然后,阻塞这个节点的线程(当前线程) 64 */ 65 private boolean acquireQueued(Node node, int arg) { 66 boolean failed = true; 67 try { 68 boolean interrupted = false; 69 for (; ; ) { 70 final Node p = node.predecessor(); 71 // 新节点的前个节点是头结点,如果头结点的线程释放,新节点接可以直接执行 72 // 所有不要着急阻塞,在判断一次,头结点释放没有,如果头结点释放,新节点不阻塞,把新节点设为头结点 73 // 当新节点没有排队直接运行了,之后要将节点标记为无效 cancelAcquire 74 if (p == head && tryAcquire(arg)) { 75 // 想了很久这段代码发生的情况 76 // 这段代码发生的情况 77 // 1.node在入队列时,有不同的线程在获得了锁,且队列中没有节点 78 // 2.当执行到这里再次tryAcquire之前,之前释放了锁 79 // 3.这时hasQueuedPredecessors中的判断,头结点的后一个节点,是新建的这个节点,满足s.thread==Thread.currentThread(不考虑这时有其他线程进入,或者进入无效) 80 // 满足了tryAcquire返回true的情况 81 // 将头结点改为新节点 82 /**** 83 * head tail 84 * | | 85 * | | 86 * ---------- --------- 87 * nullNode newNode 88 * --------- ---------- 89 * next=newNode prev=nullNode 90 * prev=null next=null 91 * ------- ---------- 92 * 93 * 改完后 94 * 95 * head tail 96 * | | 97 * | | 98 * --------- --------- 99 * nullNode newNode100 * --------- ---------101 * next=newNode prev=nullNode102 * prev=null next=null103 * --------- ----------104 * */105 106 setHead(node);107 p.next = null;108 failed = false;109 return interrupted;110 }111 // 之前的节点不是正在执行线程的节点,调整位置和状态再阻塞112 // 在线程解除阻塞后,使者节点失效113 if (shouldParkAfterFailedAcquire(p, node) &&114 parkAndCheckInterrupt())115 interrupted = true;116 }117 } finally {118 if (failed)119 // 节点解除阻塞后,可能是中断或者超时120 // 非unlock的解锁121 cancelAcquire(node);122 }123 }124 125 private void cancelAcquire(Node node) {126 if (node == null)127 return;128 node.thread = null;129 Node pred = node.prev;130 // 那个空的节点会保证终止131 while (pred.waitStatus > 0)132 // 将节点的prev关联到最近的有效节点133 node.prev = pred = pred.prev;134 Node predNext = pred.next;135 // 任何情况都执行的136 node.waitStatus = Node.CANCELLED;137 138 // 如果取消的节点是队尾节点,并且将前节点设为队尾节点139 if (node == tail && compareAndSetTail(node, pred)) {140 // cancel的节点和cancel之前的无效节点会移出队列141 compareAndSetNext(pred, predNext, null);142 } else {143 // 如果不是队尾节点144 int ws;145 if (pred != head &&146 ((ws = pred.waitStatus) == Node.SIGNAL ||147 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&148 pred.thread != null) {149 Node next = node.next;150 if (next != null && next.waitStatus <= 0)151 // prev->node->next 改为 prev->next152 compareAndSetNext(pred, predNext, next);153 } else {154 // 判断锁定的状态155 // 如果前节点是头结点,或者不是SIGNAL状态并且无法设置为SIGNAL状态156 // 总结,取消一个节点是,要保证这个节点能被释放,要不通过前节点通知,在锁锁,对应release157 unparkSuccessor(node);158 }159 160 node.next = node; // help GC161 }162 }163 164 private void unparkSuccessor(Node node) {165 // 解锁节点的线程166 // 当node时头节点时,是当前获取线程释放的炒作167 // 不是偷节点168 int ws = node.waitStatus;169 if (ws < 0)170 // 不用再去通知下个节点了,即将释放node了171 compareAndSetWaitStatus(node, ws, 0);172 Node s = node.next;173 if (s == null || s.waitStatus > 0) {174 s = null;175 // 从队尾向前找到最前有效的节点176 for (Node t = tail; t != null && t != node; t = t.prev)177 if (t.waitStatus <= 0)178 s = t;179 }180 if (s != null)181 LockSupport.unpark(s.thread);182 183 }184 185 private void compareAndSetNext(Node pred, Node predNext, Object o) {186 187 }188 189 private boolean parkAndCheckInterrupt() {190 // 阻塞191 LockSupport.park(this);192 // 当前前程标记中断193 return Thread.interrupted();194 }195 196 private boolean shouldParkAfterFailedAcquire(Node pred, Node node) {197 int ws = pred.waitStatus;198 // 如果前节点是需要被通知的,前节点正在被阻塞,阻塞当先线程199 if (ws == Node.SIGNAL)200 return true;201 // 如果前节点是无效的,找到最近的一个有效节点,并关联,返回,在外部调用方法中会再次调用这个方法202 if (ws > 0) {203 do {204 node.prev = pred = pred.prev;205 } while (pred.waitStatus > 0);206 // 这是个切断调用链的过程207 pred.next = node;208 } else {209 // 更新前节点的状态,释放时通知新节点210 compareAndSetWaitStatus(pred, ws, Node.SIGNAL);211 }212 return false;213 }214 215 /**216 * 创建节点217 * 节点入队218 *219 * @return 新节点220 */221 private Node addWaiter(Node mode) {222 Node node = new Node(Thread.currentThread(), mode);223 Node pred = tail;224 // 之前有节点在队列中225 if (pred != null) {226 node.prev = pred;227 // 直接修改队尾,不成功要进入接下类的循环,循环中也有类型的判断,这里添加会减少一些逻辑(这样说可能是理解的有偏差)228 if (compareAndSetTail(pred, node)) {229 pred.next = node;230 return node;231 }232 }233 enq(node);234 return node;235 }236 237 /**238 * 节点入队239 * 循环,直到把新节点放到队尾,在多线程中这个过程是不确定的240 */241 private Node enq(Node node) {242 for (; ; ) {243 Node t = tail;244 // Must initialize245 // 队尾没值,新节点是第一个入队的节点,创建一个空的节点,头尾都指向这个空节点246 if (t == null) {247 if (compareAndSetHead(new Node()))248 tail = head;249 } else {250 node.prev = t;251 if (compareAndSetTail(t, node)) {252 t.next = node;253 return t;254 }255 }256 }257 }258 259 /**260 * 字面理解,是否有已经排队的线程261 * 实际意义,有重入锁的情况,在这里要考虑到262 * 没有节点在排队的情况,头结点与未节点是相同的263 * 判断重入,当前线程是头结点的线程.264 */265 protected boolean hasQueuedPredecessors() {266 Node t = tail;267 Node h = head;268 Node s;269 //为什么是头结点的线程,而不是exclusiveOwnerThread,因为只有在270 // 当前队列里没有值得时候才回设置独占线程,如果是通过节点释放的线271 // 程还会和节点绑定,不会映射到exclusiveOwnerThread272 return h != t &&273 ((s = h.next) == null || s.thread != Thread.currentThread());274 }275 276 public final boolean release(int arg) {277 if (tryRelease(arg)) {278 Node h = head;279 // 在独占锁的时候,waitStatus只能为0 -1 -2 -3280 // 这个里不为0代表头节点是空节点281 // 空节点不需要释放282 // 头节点是释放锁的时候,最先被考虑的283 if (h != null && h.waitStatus != 0)284 unparkSuccessor(h);285 return true;286 }287 return false;288 }289 290 protected abstract boolean tryRelease(int arg);291 292 293 public void setHead(Node head) {294 this.head = head;295 }296 297 private boolean compareAndSetHead(Node node) {298 return (true || false);299 }300 301 private boolean compareAndSetTail(Node pred, Node node) {302 return (true || false);303 }304 305 protected void selfInterrupt() {306 Thread.currentThread().interrupt();307 }308 309 310 /**311 * CAS更新队列状态,CAS的问题在其他的机会介绍312 */313 boolean compareAndSetState(int o, int n) {314 return (false || true);315 }316 317 /**318 * 独占线程标记改为指定线程319 */320 void setExclusiveOwnerThread(Thread t) {321 exclusiveOwnerThread = t;322 }323 324 /**325 * 返回独占线程326 */327 Thread getExclusiveOwnerThread() {328 return exclusiveOwnerThread;329 }330 331 // 修改节点的状态332 private boolean compareAndSetWaitStatus(Node pred, int ws, int signal) {333 return (true || false);334 }335 336 static class Node {337 338 public int waitStatus;339 340 Node() {341 }342 343 /**344 * @param thread345 * @param mode SHARED or EXCLUSIVE346 */347 Node(Thread thread, Node mode) {348 this.thread = Thread.currentThread();349 this.mode = mode;350 }351 352 // 共享模式标记353 static final Node SHARED = new Node();354 // 独占模式标记355 static final Node EXCLUSIVE = null;356 357 // 节点被取消,因为超时或者中断358 static final int CANCELLED = 1;359 // next被阻塞,当节点释放时,notice next360 static final int SIGNAL = -1;361 // 在条件队列中,等待某个条件被阻塞362 static final int CONDITION = -2;363 // 节点在共享模式下,可以传播锁364 static final int PROPAGATE = -3;365 366 volatile Node next;367 volatile Node prev;368 Node mode;369 370 public Thread thread;371 372 public Node predecessor() {373 Node p = prev;374 if (p == null)375 throw new NullPointerException();376 else377 return p;378 }379 }380 381 382 }383 384 /**385 * 这是一个独占锁的实现,从ReentrantLock中粘贴出来的部分代码386 */387 class SYC extends AQS {388 389 public void lock() {390 acquire(1);391 }392 393 public void unlock() {394 release(1);395 }396 397 protected final boolean tryAcquire(int acquires) {398 final Thread current = Thread.currentThread();399 int c = getState();400 // 如果当前的状态401 if (c == 0) {402 if (!hasQueuedPredecessors() &&403 compareAndSetState(0, acquires)) {404 setExclusiveOwnerThread(current);405 return true;406 }407 } else if (current == getExclusiveOwnerThread()) {408 int nextc = c + acquires;409 if (nextc < 0)410 throw new Error("Maximum lock count exceeded");411 setState(nextc);412 return true;413 }414 return false;415 }416 417 protected final boolean tryRelease(int releases) {418 int c = getState() - releases;419 if (Thread.currentThread() != getExclusiveOwnerThread())420 throw new IllegalMonitorStateException();421 boolean free = false;422 if (c == 0) {423 free = true;424 setExclusiveOwnerThread(null);425 }426 setState(c);427 return free;428 }429 430 431 }432 }
海外公司注册、海外银行开户、跨境平台代入驻、VAT、EPR等知识和在线办理:https://www.xlkjsw.com
原标题:基于ReentrantLock的AQS的源码分析(独占、非中断、不超时部分)
关键词:AQS
*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们:
admin#shaoqun.com
(#换成@)。