最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

CountDownLatch源碼解析之countDown()

 更新時間:2018年04月01日 15:10:44   作者:貓咪大王  
這篇文章主要為大家詳細解析了CountDownLatch源碼之countDown方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下

CountDownLatch 源碼解析—— countDown()

上一篇文章從源碼層面說了一下CountDownLatch 中 await() 的原理。這篇文章說一下countDown() 。

public void countDown() { //CountDownLatch
 sync.releaseShared(1);
}
 ↓
public final boolean releaseShared(int arg) { //AQS
 if (tryReleaseShared(arg)) {
  doReleaseShared();
  return true;
 }
 return false;
}
 ↓
protected boolean tryReleaseShared(int releases) { //CountDownLatch.Sync 
 // Decrement count; signal when transition to zero
 for (;;) {
  int c = getState();
  if (c == 0)
   return false;
  int nextc = c-1;
  if (compareAndSetState(c, nextc))
   return nextc == 0;
 }
}

通過構(gòu)造器 CountDownLatch end = new CountDownLatch(2);  state 被設(shè)置為2,所以c == 2,nextc = 2-1,

然后通過下面這個CAS操作將state設(shè)置為1。

protected final boolean compareAndSetState(int expect, int update) {
  // See below for intrinsics setup to support this
  return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
 }

此時nextc還不為0,返回false。一直等到countDown()  方法被調(diào)用兩次,state == 0,nextc ==0,此時返回true。

進入doReleaseShared()方法。

doReleaseShared();
 ↓
private void doReleaseShared() {
 /*
  * Ensure that a release propagates, even if there are other
  * in-progress acquires/releases. This proceeds in the usual
  * way of trying to unparkSuccessor of head if it needs
  * signal. But if it does not, status is set to PROPAGATE to
  * ensure that upon release, propagation continues.
  * Additionally, we must loop in case a new node is added
  * while we are doing this. Also, unlike other uses of
  * unparkSuccessor, we need to know if CAS to reset status
  * fails, if so rechecking.
  */
 for (;;) {
  Node h = head;
  if (h != null && h != tail) {
   int ws = h.waitStatus;
   if (ws == Node.SIGNAL) {
    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
     continue;   // loop to recheck cases
    unparkSuccessor(h);
   }
   else if (ws == 0 &&
      !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
    continue;    // loop on failed CAS
  }
  if (h == head)     // loop if head changed
   break;
 }
}

回顧一下此時的等待隊列模型。

  +--------------------------+ prev   +------------------+
head | waitStatus = Node.SIGNAL | <---- node(tail) | currentThread |
  +--------------------------+     +------------------+

此時head 不為null,也不為tail,waitStatus == Node.SIGNAL,所以進入 if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) 這個判斷。

if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
 ↓
 /**
 * CAS waitStatus field of a node.
 */
private static final boolean compareAndSetWaitStatus(Node node,
              int expect,
              int update) {
 return unsafe.compareAndSwapInt(node, waitStatusOffset,
         expect, update);
}

這個CAS 操作將 state 設(shè)置為 0 ,也就是說此時Head 中的 waitStatus 是0.此時隊列模型如下所示

  +----------------+ prev   +------------------+
head | waitStatus = 0 | <---- node(tail) | currentThread |
  +----------------+     +------------------+

該方法返回true。進入unparkSuccessor(h);

unparkSuccessor(h);
 ↓
private void unparkSuccessor(Node node) {
 /*
 * If status is negative (i.e., possibly needing signal) try
 * to clear in anticipation of signalling. It is OK if this
 * fails or if status is changed by waiting thread.
 */
 int ws = node.waitStatus;
 if (ws < 0)
  compareAndSetWaitStatus(node, ws, 0);

 /*
 * Thread to unpark is held in successor, which is normally
 * just the next node. But if cancelled or apparently null,
 * traverse backwards from tail to find the actual
 * non-cancelled successor.
 */
 Node s = node.next;
 if (s == null || s.waitStatus > 0) {
  s = null;
  for (Node t = tail; t != null && t != node; t = t.prev)
   if (t.waitStatus <= 0)
    s = t;
 }
 if (s != null)
  LockSupport.unpark(s.thread);
}

s 就是head的后繼結(jié)點,也就是裝有當(dāng)前線程的結(jié)點。s != null ,并且s.waitStatus ==0 ,所以進入 LockSupport.unpark(s.thread);

 public static void unpark(Thread thread) {
  if (thread != null)
   UNSAFE.unpark(thread);
 }

也就是unlock 被阻塞的線程。裁判被允許吹哨了!

countDown() 的原理就此就非常清晰了。

每執(zhí)行一次countDown() 方法,state 就是減1,直到state == 0,則開始釋放被阻塞在隊列中的線程,根據(jù)前驅(qū)結(jié)點中waitStatus的狀態(tài),釋放后續(xù)結(jié)點中的線程。

OK,回到上一篇文章的問題,什么時候跳出下面這個循環(huán)(await方法中的循環(huán))

for (;;) {
 final Node p = node.predecessor();
 if (p == head) {
  int r = tryAcquireShared(arg);
  if (r >= 0) {
   setHeadAndPropagate(node, r);
   p.next = null; // help GC
   failed = false;
   return;
  }
 }
 if (shouldParkAfterFailedAcquire(p, node) &&
  parkAndCheckInterrupt())
  throw new InterruptedException();
}

此時state == 0,所以進入 setHeadAndPropagate 方法。

setHeadAndPropagate(node, r);
 ↓
private void setHeadAndPropagate(Node node, int propagate) {
 Node h = head; // Record old head for check below
 setHead(node);
 /*
  * Try to signal next queued node if:
  * Propagation was indicated by caller,
  *  or was recorded (as h.waitStatus either before
  *  or after setHead) by a previous operation
  *  (note: this uses sign-check of waitStatus because
  *  PROPAGATE status may transition to SIGNAL.)
  * and
  * The next node is waiting in shared mode,
  *  or we don't know, because it appears null
  *
  * The conservatism in both of these checks may cause
  * unnecessary wake-ups, but only when there are multiple
  * racing acquires/releases, so most need signals now or soon
  * anyway.
  */
 if (propagate > 0 || h == null || h.waitStatus < 0 ||
  (h = head) == null || h.waitStatus < 0) {
  Node s = node.next;
  if (s == null || s.isShared())
   doReleaseShared();
 }
}
 ↓
private void setHead(Node node) {
 head = node;
 node.thread = null;
 node.prev = null;
}

這個方法將head 的后繼結(jié)點變?yōu)閔ead。該方法過后,又將node的next結(jié)點設(shè)置為null,模型變成下圖

  prev    +---------+ next
null <---- node(tail/head) | null | ----> null
       +---------+

也就是node head tail 什么的都被置為null,等待GC回收了,這個時候return,跳出了for循環(huán),隊列被清空。

下面演示一下整個過程

setHeadAndPropagate(node, r);

   +----------------+ 
head(tail) | waitStatus=0 |
   | thread =null |
   +----------------+
     ↓
   +----------------+   +----------------+
   | waitStatus=0 | prev  | waitStatus=0 |
head(tail) | thread =null | <---- node | currentThread |
   +----------------+   +----------------+   
     ↓
  +----------------+     +----------------+
  | waitStatus=0 | prev   | waitStatus=0 |
head | thread =null | <---- node(tail) | currentThread |
  +----------------+     +----------------+
     ↓
  +----------------+     +----------------+
  | Node.SIGNAL | prev   | waitStatus=0 |
head | thread =null | <---- node(tail) | currentThread |
  +----------------+     +----------------+
       ↓
  +----------------+     +----------------+
  | waitStatus=0 | prev   | waitStatus=0 |
head | thread =null | <---- node(tail) | currentThread |
  +----------------+     +----------------+
       ↓
       +----------------+
  prev    | waitStatus=0 | next
null <---- node(tail/head) | null   | ----> null
       +----------------+

CountDownLatch 的核心就是一個阻塞線程隊列,這是由鏈表構(gòu)造而成的隊列,里面包含thread 和 waitStatus,其中waitStatus說明了后繼結(jié)點線程狀態(tài)。

state 是一個非常重要的標志,構(gòu)造時,設(shè)置為對應(yīng)的n值,如果n != 0,阻塞隊列將一直阻塞,除非中斷線程。

每次調(diào)用countDown()  方法,就是將state-1,而調(diào)用await() 方法就是將調(diào)用該方法的線程加入到阻塞隊列,直到state==0,才能釋放線程。

 以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java+TestNG接口自動化入門詳解

    Java+TestNG接口自動化入門詳解

    本文主要介紹了Java+TestNG接口自動化入門,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • java中BIO、NIO、AIO都有啥區(qū)別

    java中BIO、NIO、AIO都有啥區(qū)別

    這篇文章主要介紹了java中BIO、NIO、AIO都有啥區(qū)別,IO模型就是說用什么樣的通道進行數(shù)據(jù)的發(fā)送和接收,Java共支持3種網(wǎng)絡(luò)編程IO模式:BIO,NIO,AIO,文中有非常詳細的代碼示例,對正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • java字符串反轉(zhuǎn)示例分享

    java字符串反轉(zhuǎn)示例分享

    這篇文章主要介紹了將一個字符串進行反轉(zhuǎn)或者字符串中指定部分進行反轉(zhuǎn)的方法,大家參考使用吧
    2014-01-01
  • Mybatis之動態(tài)sql標簽的使用

    Mybatis之動態(tài)sql標簽的使用

    這篇文章主要介紹了Mybatis之動態(tài)sql標簽的使用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • 通過實例解析Java分布式鎖三種實現(xiàn)方法

    通過實例解析Java分布式鎖三種實現(xiàn)方法

    這篇文章主要介紹了通過實例解析Java分布式鎖三種實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-07-07
  • java定位死鎖的三種方法(jstack、Arthas和Jvisualvm)

    java定位死鎖的三種方法(jstack、Arthas和Jvisualvm)

    這篇文章主要給大家介紹了關(guān)于java定位死鎖的三種方法,分別是通過jstack定位死鎖信息、通過Arthas工具定位死鎖以及通過 Jvisualvm 定位死鎖,文中還介紹了死鎖的預(yù)防方法,需要的朋友可以參考下
    2021-09-09
  • Java 實現(xiàn)Redis存儲復(fù)雜json格式數(shù)據(jù)并返回給前端

    Java 實現(xiàn)Redis存儲復(fù)雜json格式數(shù)據(jù)并返回給前端

    這篇文章主要介紹了Java 實現(xiàn)Redis存儲復(fù)雜json格式數(shù)據(jù)并返回給前端操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-07-07
  • java圖片和文本同時提交到表單的實例代碼

    java圖片和文本同時提交到表單的實例代碼

    在本篇文章里小編給大家整理的是關(guān)于java實現(xiàn)圖片和文本同時提交到表單的相關(guān)內(nèi)容,有需要的朋友們可以學(xué)習(xí)下。
    2020-02-02
  • 詳解java 中Spring jsonp 跨域請求的實例

    詳解java 中Spring jsonp 跨域請求的實例

    這篇文章主要介紹了詳解java 中Spring jsonp 跨域請求的實例的相關(guān)資料,jsonp 可用于解決主流瀏覽器的跨域數(shù)據(jù)訪問的問題,需要的朋友可以參考下
    2017-08-08
  • JavaAPI的使用方法詳解

    JavaAPI的使用方法詳解

    這篇文章主要介紹了JavaAPI的使用方法詳解,還是比較不錯的,這里分享給大家,供需要的朋友參考。
    2017-11-11

最新評論

秀山| 长岭县| 平湖市| 沈阳市| 桐柏县| 武山县| 武威市| 邢台县| 苏尼特左旗| 滨州市| 芒康县| 嘉义市| 喀什市| 蕲春县| 龙川县| 青阳县| 云霄县| 综艺| 子洲县| 文昌市| 隆昌县| 祁阳县| 义马市| 中阳县| 榆林市| 海南省| 内黄县| 哈巴河县| 四会市| 阿城市| 屏边| 宽城| 平南县| 博兴县| 会宁县| 晋江市| 文昌市| 临猗县| 苏尼特左旗| 建昌县| 永泰县|