主頁 >  其他 > Java 并發關鍵字大練兵—一文讀懂各個關鍵字

Java 并發關鍵字大練兵—一文讀懂各個關鍵字

2020-12-14 12:44:19 其他

本文介紹了Threadlocal、volatile、condition、Semaphore、CountDownLatch、unsafe 等關鍵字

目錄如下:

  • Threadlocal 本地執行緒
  • volatile
  • condition
  • CountDownLatch 閂鎖
  • CyclicBarrier 籬柵
  • Semaphore 信號燈
  • unsafe 魔法類
  • StampedLock 新讀寫鎖

1. Threadlocal

從名字我們就可以看到ThreadLocal叫做本地執行緒,意思是ThreadLocal中填充的變數屬于當前執行緒,該變數對其他執行緒而言是隔離的,ThreadLocal為變數在每個執行緒中都創建了一個副本,那么每個執行緒可以訪問自己內部的副本變數,

Java就是通過ThreadLocal來實作執行緒本地存盤的,

使用場景:

  • 在進行物件跨層傳遞的時候,使用ThreadLocal可以避免多次傳遞,打破層次間的約束,
  • 執行緒間資料隔離
  • 進行事務操作,用于存盤執行緒事務資訊,
  • 資料庫連接,Session會話管理,**

1.1 ThreadLocal 結構分析

ThreadLocalMap里面有個Entry陣列,只有陣列沒有像HashMap那樣有鏈表,因此當hash沖突的之后,ThreadLocalMap采用線性探測的方式解決hash沖突,

線性探測,就是先根據初始keyhashcode值確定元素在table陣列中的位置,如果這個位置上已經有其他key值的元素被占用,則利用固定的演算法尋找一定步長的下個位置,依次直至找到能夠存放的位置,在ThreadLocalMap步長是1,

線性探測 是通過 AtomicInteger 的原子性方法 getAndAdd 獲取個位置陣列中的位置,如果該位置已被占用則通過 nextIndex 方法獲取下一個位置,回圈直到有空位置為止,

結構如下

public class ThreadLocal<T> {
   private static AtomicInteger nextHashCode =
        new AtomicInteger();  
    private static final int HASH_INCREMENT = 0x61c88647;

    
    private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }
    private static int nextIndex(int i, int len) {
            return ((i + 1 < len) ? i + 1 : 0);
    }
   static class ThreadLocalMap {
      private Entry[] table;
      private int size = 0;
     //繼承弱參考
      static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
              //對k加上弱參考WeakReference
                super(k);
                value = v;
            }
        }
        
 		private void set(ThreadLocal<?> key, Object value) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);
			//回圈探測下個hashcode 的位置
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }
   }
}

1.2 ThreadLocalMap

ThreadLocalMap其實就是ThreadLocal的一個靜態內部類(多執行緒共享),里面定義了一個Entry[]陣列來保存資料,而且還是繼承的弱參考, key為弱參考,在Entry內部使用ThreadLocal作為key,使用我們設定的value作為value,

  • 每個Thread維護著一個ThreadLocalMap的參考
  • ThreadLocalMap是ThreadLocal的內部類,用Entry來進行存盤
  • ThreadLocal創建的副本是存盤在自己的threadLocals中的,也就是自己的ThreadLocalMap,
  • ThreadLocalMap的鍵值為ThreadLocal物件,而且可以有多個threadLocal變數,因此保存在map中
  • 在進行get之前,必須先set,否則會報空指標例外,當然也可以初始化一個,但是必須重寫initialValue()方法,
  • ThreadLocal本身并不存盤值,它只是作為一個key來讓執行緒從ThreadLocalMap獲取value,

1.3 記憶體泄漏

記憶體泄漏問題:

上面這張圖詳細的揭示了ThreadLocal和Thread以及ThreadLocalMap三者的關系,

  • 1、Thread中有一個map,就是ThreadLocalMap
  • 2、ThreadLocalMap的key是ThreadLocal,值是我們自己設定的,
  • 3、ThreadLocal是一個弱參考weakReference,當為null時,會被當成垃圾回收
  • 4、重點來了,突然我們ThreadLocal是null了,也就是要被垃圾回收器回收了,但是此時我們的ThreadLocalMap生命周期和Thread的一樣,它不會回收,這時候就出現了一個現象,那就是ThreadLocalMap的key沒了,但是value還在,這就造成了記憶體泄漏,

解決辦法:使用完ThreadLocal后,執行remove操作,避免出現記憶體溢位情況,

參考:
https://mp.weixin.qq.com/s/Gc1YPt_DPMNKbbE_I0jmSA
https://baijiahao.baidu.com/s?id=1653790035315010634&wfr=spider&for=pc

2. volatile

  • 保證了不同執行緒對這個變數進行操作時的可見性,即一個執行緒修改了某個變數的值,這新值對其他執行緒來說是立即可見的,(實作可見性)
  • 禁止進行指令重排序,(實作有序性)
  • volatile 只能保證對單次讀/寫的原子性,i++ 這種操作不能保證原子性

讀理解

當讀一個volatile變數時,JMM會把該執行緒對應的本地記憶體置為無效,執行緒接下來將從主記憶體中讀取共享變數,

寫理解

當寫一個volatile變數時,JMM會把該執行緒對應的本地中的共享變數值重繪到主記憶體,

將當前處理器快取行的資料寫回到系統記憶體,這個寫回記憶體的操作會告知在其他CPU你們拿到的變數是無效的下一次使用時候要重新共享記憶體拿,

使用 volatile 必須具備的條件

  • 對變數的寫操作不依賴于當前值,

該變數沒有包含在具有其他變數的不變式中,

  • 只有在狀態真正獨立于程式內其他內容時才能使用 volatile,

2.1 volatile 禁止指令重排

volatile是通過記憶體屏障來來禁止指令重排的,

記憶體屏障(Memory Barrier是一類同步屏障指令,是CPU或編譯器在對記憶體隨機訪問的操作中的一個同步點,使得此點之前的所有讀寫操作都執行后才可以開始執行此點之后的操作,下表描述了和volatile有關的指令重排禁止行為:

記憶體屏障就是基于4個匯編級別的關鍵字來禁止指令重排的,其中volatile的規則如下:

在這里插入圖片描述

  • 當第二個操作是volatile寫時,不管第一個操作是什么,都不能重排序,這個規則確保volatile寫之前的操作不會被編譯器重排序到volatile寫之后,

  • 當第一個操作是volatile讀時,不管第二個操作是什么,都不能重排序,這個規則確保volatile讀之后的操作不會被編譯器重排序到volatile讀之前,

  • 當第一個操作是volatile寫,第二個操作是volatile讀時,不能重排序,

2.1.1 記憶體屏障(Memory Barrier)

在這里插入圖片描述

StoreStore屏障:禁止上面的普通寫和下面的volatile寫重排序;

StoreLoad屏障:防止上面的volatile寫與下面可能有的volatile讀/寫重排序

LoadLoad屏障:禁止下面所有的普通讀操作和上面的volatile讀重排序

LoadStore屏障:禁止下面所有的普通寫操作和上面的volatile讀重排序

CPU中,每個CPU又有多級快取,一般分為L1,L2,L3,因為這些快取的出現,提高了資料訪問性能,避免每次都向記憶體索取,但是弊端也很明顯,不能實時的和記憶體發生資訊交換,分在不同CPU執行的不同執行緒對同一個變數的快取值不同,

  • 硬體層的記憶體屏障分為兩種:Load BarrierStore Barrier即讀屏障和寫屏障,【記憶體屏障是硬體層的】

2.1.2 為什么需要記憶體屏障

由于現代作業系統都是多處理器作業系統,每個處理器都會有自己的快取,可能存再不同處理器快取不一致的問題,而且由于作業系統可能存在指令重排序,導致讀取到錯誤的資料,因此,作業系統提供了一些記憶體屏障以解決這種問題.
簡單來說:
1.在不同CPU執行的不同執行緒對同一個變數的快取值不同,為了解決這個問題,用volatile可以解決上面的問題,不同硬體對記憶體屏障的實作方式不一樣,java屏蔽掉這些差異,通過jvm生成記憶體屏障的指令,

2.對于讀屏障:在指令前插入讀屏障,可以讓高速快取中的資料失效,強制從主記憶體取,

2.1.3 記憶體屏障的作用

cpu執行指令可能是無序的,它有兩個比較重要的作用
1.阻止屏障兩側指令重排序
2.強制把寫緩沖區/高速快取中的臟資料等寫回主記憶體,讓快取中相應的資料失效,

2.2有序性

編譯器在生成位元組碼時,會在指令序列中插入記憶體屏障來禁止特定型別的處理器重排序,對于編譯器來說,發現一個最優布置來最小化插入屏障的總數幾乎是不可能的,為此,JMM采取了保守策略:

  1. 在每個volatile寫操作的前面插入一個StoreStore屏障;寫完對寫可見
    • 對于這樣的陳述句Store1; StoreStore; Store2,在Store2及后續寫入操作執行前,保證Store1的寫入操作對其它處理器可見
  2. 在每個volatile寫操作后面插入一個StoreLoad屏障;寫完對讀可見
    • 對于這樣的陳述句Store1; StoreLoad; Load2,在Load2及后續所有讀取操作執行前,保證Store1的寫入對所有處理器可見
  3. 在每個volatile讀操作的后面插入一個LoadLoad屏障;讀完對讀可見
    • 對于這樣的陳述句Load1; LoadLoad; Load2,在Load2及后續讀取操作要讀取的資料被訪問前,保證Load1要讀取的資料被讀取完畢
  4. 在每個volatile讀操作之后再插入一個LoadStore屏障,讀完對寫可見
    • 對于這樣的陳述句Load1; LoadStore; Store2,在Store2及后續寫入操作被刷出前,保證Load1要讀取的資料被讀取完畢,

volatile通過在volatile變數的操作前后插入記憶體屏障的方式,來禁止指令重排,進而保證多執行緒情況下對共享變數的有序性,

2.3 volatile 可見性

volatile對于可見性的實作,記憶體屏障也起著至關重要的作用,因為記憶體屏障相當于一個資料同步點,他要保證在這個同步點之后的讀寫操作必須在這個點之前的讀寫操作都執行完之后才可以執行,并且在遇到記憶體屏障的時候,快取資料會和主存進行同步,或者把快取資料寫入主存、或者從主存把資料讀取到快取

作業系統中的快取和JVM中執行緒的本地記憶體并不是一回事,通常我們可以認為:MESI可以解決快取層面的可見性問題,使用volatile關鍵字,可以解決JVM層面的可見性問題,

快取可見性問題的延伸:由于傳統的MESI協議的執行成本比較大,所以CPU通過Store Buffer和Invalidate Queue組件來解決,但是由于這兩個組件的引入,也導致快取和主存之間的通信并不是實時的,也就是說,快取一致性模型只能保證快取變更可以保證其他快取也跟著改變,但是不能保證立刻、馬上執行,

在計算機記憶體模型中,也是使用記憶體屏障來解決快取的可見性問題的(再次強調:快取可見性和并發編程中的可見性可以互相類比,但是他們并不是一回事兒),寫記憶體屏障(Store Memory Barrier)可以促使處理器將當前store buffer(存盤快取)的值寫回主存,讀記憶體屏障(Load Memory Barrier)可以促使處理器處理invalidate queue(失效佇列),進而避免由于Store Buffer和Invalidate Queue的非實時性帶來的問題,

記憶體屏障也是保證可見性的重要手段,作業系統通過記憶體屏障保證快取間的可見性,JVM通過給volatile變數加入記憶體屏障保證執行緒之間的可見性,

2.4 volatile 與 synchronized 的比較

  • volatile是變數修飾符,其修飾的變數具有可見性,
  • volatile主要用在多個執行緒感知實體變數被更改了場合,從而使得各個執行緒獲得最新的值,它強制執行緒每次從主記憶體中取到變數,而不是從執行緒的私有記憶體中讀取變數,從而保證了資料的可見性,
  • ①volatile輕量級,只能修飾變數,synchronized重量級,還可修飾方法
  • ②volatile只能保證資料的可見性,不能用來同步,因為多個執行緒并發訪問volatile修飾的變數不會阻塞,
  • synchronized不僅保證可見性,而且還保證原子性,因為,只有獲得了鎖的執行緒才能進入臨界區,從而保證臨界區中的所有陳述句都全部執行,多個執行緒爭搶synchronized鎖物件時,會出現阻塞,

參考:https://www.hollischuang.com/archives/2673?spm=a2c6h.12873639.0.0.1c786ca5UcOGO5

3. Condition

在 lock 介面和 AbstractQueuedSynchronizer 中的ConditionObject類都有用到 condition 介面

interface Lock { 
 Condition newCondition();
}

在使用Lock之前,我們使用的最多的同步方式應該是synchronized關鍵字來實作同步方式了,配合Object的wait()、notify()系列方法可以實作等待/通知模式,Condition介面也提供了類似Object的監視器方法,與Lock配合可以實作等待/通知模式,但是這兩者在使用方式以及功能特性上還是有差別的,Object和Condition介面的一些對比,

在這里插入圖片描述

condition物件是依賴于lock物件的,意思就是說condition物件需要通過lock物件進行創建出來(呼叫Lock物件的newCondition()方法),但是需要注意在呼叫方法前獲取鎖,

一般都會將Condition物件作為成員變數,當呼叫await()方法后,當前執行緒會釋放鎖并在此等待,而其他執行緒呼叫Condition物件的signal()方法,通知當前執行緒后,當前執行緒才從await()方法回傳,并且在回傳前已經獲取了鎖,

3.1 condition常用方法

condition可以通俗的理解為條件佇列,當一個執行緒在呼叫了await方法以后,直到執行緒等待的某個條件為真的時候才會被喚醒,這種方式為執行緒提供了更加簡單的等待/通知模式,Condition必須要配合鎖一起使用,因為對共享狀態變數的訪問發生在多執行緒環境下,一個Condition的實體必須與一個Lock系結,因此Condition一般都是作為Lock的內部實作,

  • await() :造成當前執行緒在接到信號或被中斷之前一直處于等待狀態,
  • await(long time, TimeUnit unit) :造成當前執行緒在接到信號、被中斷或到達指定等待時間之前一直處于等待狀態
  • awaitNanos(long nanosTimeout) :造成當前執行緒在接到信號、被中斷或到達指定等待時間之前一直處于等待狀態,回傳值表示剩余時間,如果在nanosTimesout之前喚醒,那么回傳值 = nanosTimeout - 消耗時間,如果回傳值 <= 0 ,則可以認定它已經超時了,
  • awaitUninterruptibly() :造成當前執行緒在接到信號之前一直處于等待狀態,【注意:該方法對中斷不敏感】,
  • awaitUntil(Date deadline) :造成當前執行緒在接到信號被中斷或到達指定最后期限之前一直處于等待狀態,如果沒有到指定時間就被通知,則回傳true,否則表示到了指定時間,回傳false,
  • signal() :喚醒一個等待執行緒,該執行緒從等待方法回傳前必須獲得與Condition相關的鎖,
  • signal()All :喚醒所有等待執行緒,能夠從等待方法回傳的執行緒必須獲得與Condition相關的鎖,

3.2 condition原理:

Condition是AQS的內部類,每個Condition物件都包含一個佇列(等待佇列),等待佇列是一個FIFO的佇列,在佇列中的每個節點都包含了一個執行緒參考,該執行緒就是在Condition物件上等待的執行緒,如果一個執行緒呼叫了Condition.await()方法,那么該執行緒將會釋放鎖、構造成節點加入等待佇列并進入等待狀態,等待佇列的基本結構如下所示,

在這里插入圖片描述

等待分為首節點尾節點,當一個執行緒呼叫Condition.await()方法,將會以當前執行緒構造節點,并將節點從尾部加入等待佇列,新增節點后將尾部節點換為新增的節點,節點參考更新本來就是在獲取鎖以后的操作,所以不需要CAS保證,也是執行緒安全的操作,

等待

當執行緒呼叫了await方法以后,執行緒就作為佇列中的一個節點被加入到等待佇列中去了,同時會釋放鎖的占用,當從await方法回傳的時候,一定會獲取condition相關聯的鎖,當等待佇列中的節點被喚醒的時候,則喚醒節點的執行緒開始嘗試獲取同步狀態(鎖),如果不是通過其他執行緒呼叫Condition.signal()方法喚醒,而是對等待執行緒進行中斷,則會拋出InterruptedException例外資訊,

通知

呼叫Condition的signal()方法,將會喚醒在等待佇列中等待最長時間的節點(條件佇列里的首節點),在喚醒節點前,會將節點移到同步佇列中,即可以去競爭獲取鎖,,當前執行緒加入到等待佇列中如圖所示:

在這里插入圖片描述

在呼叫signal()方法之前必須先判斷是否獲取到了鎖,接著獲取等待佇列的首節點,將其移動到同步佇列并且利用LockSupport喚醒節點中的執行緒,節點從等待佇列移動到同步佇列如下圖所示:

在這里插入圖片描述

被喚醒的執行緒將從await方法中的while回圈中退出,隨后加入到同步狀態的競爭當中去,成功獲取到競爭的執行緒則會回傳到await方法之前的狀態,

3.3condition 總結

呼叫await方法后,將當前執行緒加入Condition等待佇列中,當前執行緒釋放鎖,否則別的執行緒就無法拿到鎖而發生死鎖,自旋(while)掛起,不斷檢測節點是否在同步佇列中了,如果是則嘗試獲取鎖,否則掛起,當執行緒被signal方法喚醒,被喚醒的執行緒將從await()方法中的while回圈中退出來,然后呼叫acquireQueued()方法競爭同步狀態,

4. CountDownLatch(閂鎖)

CountDownLatch適用于在多執行緒的場景需要等待所有子執行緒全部執行完畢之后再做操作的場景,

初始化一個CountDownLatch實體傳參3,因為我們有3個子執行緒,每次子執行緒執行完畢之后呼叫countDown()方法給計數器-1,主執行緒呼叫await()方法后會被阻塞,直到最后計數器變為0,await()方法回傳,執行完畢,他和join()方法的區別就是join會阻塞子執行緒直到運行結束,而CountDownLatch可以在任何時候讓await()回傳,而且用ExecutorService沒法用join了,相比起來,CountDownLatch更靈活,

CountDownLatch基于AQS實作,volatile變數state維持倒數狀態,多執行緒共享變數可見

  1. CountDownLatch通過建構式初始化傳入引數實際為AQS的state變數賦值,維持計數器倒數狀態
  2. 當主執行緒呼叫await()方法時,當前執行緒會被阻塞,當state不為0時進入AQS阻塞佇列等待,
  3. 其他執行緒呼叫countDown()時,state值原子性遞減,當state值為0的時候,喚醒所有呼叫await()方法阻塞的執行緒

4.1 CountDownLatch與thread.join()的區別

join,在當前執行緒中,如果呼叫某個thread的join方法,那么當前執行緒就會被阻塞,直到thread執行緒執行完畢,當前執行緒才能繼續執行,join的原理是,不斷的檢查thread是否存活,如果存活,那么讓當前執行緒一直wait,直到thread執行緒終止,執行緒的this.notifyAll 就會被呼叫,

CountDownLatch中我們主要用到兩個方法一個是await()方法,呼叫這個方法的執行緒會被阻塞,另外一個是countDown()方法,呼叫這個方法會使計數器減一,當計數器的值為0時,因呼叫await()方法被阻塞的執行緒會被喚醒,繼續執行,

呼叫join方法需要等待thread執行完畢才能繼續向下執行,而CountDownLatch只需要檢查計數器的值為零就可以繼續向下執行,相比之下,CountDownLatch更加靈活一些,可以實作一些更加復雜的業務場景,

CountDownLatch 小例子實作:

5. CyclicBarrier(籬柵)

CyclicBarrier叫做回環屏障,它的作用是讓一組執行緒全部達到一個狀態之后再全部同時執行,而且他有一個特點就是所有執行緒執行完畢之后是可以重用的,

CountDownLatch非常相似,初始化傳入3個執行緒和一個任務,執行緒呼叫await()之后進入阻塞,計數器-1,當計數器為0時,就去執行CyclicBarrier中建構式的任務,當任務執行完畢后,喚醒所有阻塞中的執行緒,這驗證了CyclicBarrier讓一組執行緒全部達到一個狀態之后再全部同時執行的效果,

CyclicBarrier 是通過 ReentrantLock加鎖,控制count屬性的加減計數的,也是基于AQS

可重用

每個子執行緒呼叫await()計數器減為0之后才開始繼續一起往下執行,此時count會恢復到最初計數,當再次呼叫await()時,就得再次等到計數器為0之后就又一起往下執行,這就是可重用

CyclicBarrier還是基于AQS實作的,內部維護parties記錄總執行緒數,count用于計數,最開始count=parties,呼叫await()之后count原子遞減,當count為0之后,再次將parties賦值給count,這就是復用的原理,

  1. 當子執行緒呼叫await()方法時,獲取獨占鎖,同時對count遞減,進入阻塞佇列,然后釋放鎖
  2. 當第一個執行緒被阻塞同時釋放鎖之后,其他子執行緒競爭獲取鎖,操作同1
  3. 直到最后count為0,執行CyclicBarrier建構式中的任務,執行完畢之后子執行緒繼續向下執行

CyclicBarrier 小例子實作
CyclicBarrier可重用小例子實作

6. Semaphore(信號燈)

Semaphore叫做信號量,和前面兩個不同的是,他的計數器是遞增的,

稍微和前兩個有點區別,建構式傳入的初始值為0,當子執行緒呼叫release()方法時,計數器遞增,主執行緒acquire()傳參為3 則說明主執行緒一直阻塞,直到計數器為3才會回傳

Semaphore還還還是基于AQS實作的,同時獲取信號量有公平和非公平兩種策略

  1. 主執行緒呼叫acquire()方法時,用當前信號量值-需要獲取的值,如果小于0,則進入同步阻塞佇列,大于0則通過CAS設定當前信號量為剩余值,同時回傳剩余值
  2. 子執行緒呼叫release()給當前信號量值計數器+1(增加的值數量由傳參決定),同時不停的嘗試因為呼叫acquire()進入阻塞的執行緒

Semaphore 小例子實作:

7. unsafe

AtomicInteger的自增函式incrementAndGet()的原始碼時,發現自增函式底層呼叫的是unsafe.getAndAddInt(),但是由于JDK本身只有Unsafe.class,只通過class檔案中的引數名,并不能很好的了解方法的作用,所以我們通過OpenJDK 8 來查看Unsafe的原始碼:

// ------------------------- JDK 8 -------------------------
// AtomicInteger 自增方法
public final int incrementAndGet() {
  return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}

// Unsafe.class
public final int getAndAddInt(Object var1, long var2, int var4) {
  int var5;
  do {
      var5 = this.getIntVolatile(var1, var2);
  } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
  return var5;
}

// ------------------------- OpenJDK 8 -------------------------
// Unsafe.java
public final int getAndAddInt(Object o, long offset, int delta) {
   int v;
   do {
       v = getIntVolatile(o, offset);
   } while (!compareAndSwapInt(o, offset, v, v + delta));
   return v;
}

unsafe java 魔法類

8. StampedLock

在Java 8中引入了一種鎖的新機制——StampedLock,它可以看成是讀寫鎖的一個改進版本,StampedLock提供了一種樂觀讀鎖的實作,這種樂觀讀鎖類似于無鎖的操作,完全不會阻塞寫執行緒獲取寫鎖,從而**緩解讀多寫少時寫執行緒“饑餓”**現象,由于StampedLock提供的樂觀讀鎖不阻塞寫執行緒獲取讀鎖,當執行緒共享變數從主記憶體load到執行緒作業記憶體時,會存在資料不一致問題,所以當使用StampedLock的樂觀讀鎖時,需要遵從如下圖用例中使用的模式來確保資料的一致性,

小結

CountDownLatch通過計數器提供了比join更靈活的多執行緒控制方式,join會阻塞子執行緒直到運行結束,而CountDownLatch可以在任何時候讓await()回傳,CyclicBarrier也可以達到CountDownLatch的效果,而且有可復用的特點,Semaphore則是采用信號量遞增的方式,開始的時候并不需要關注需要同步的執行緒個數,并且提供獲取信號的公平和非公平策略,

參考:
unsafe java 魔法類
說說CountDownLatch,CyclicBarrier,Semaphore的原理?

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/234356.html

標籤:AI

上一篇:WIFI類物聯網產品配網方式簡述

下一篇:Kubernetes學習總結(4)——Kubernetes v1.20 重磅發布 | 新版本核心主題 & 主要變化解讀

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more