標籤:串連數 length str hang 時間長度 主線程 nis [] 訊號
CountDownLatch
CountDownLatch允許一個或多個線程等待其他線程完成操作
當需要解析一個Excel裡面有多個sheet資料時,可以使用多線程,每個線程解析一個sheet裡的資料。主線程等待所有線程執行完sheet的解析操作。
public class JoinCountDownLatchTest(){ public static void main(String[] args){ Thread parser1 = new Thread(new Runnable)_{ public void run(){ } }); Thread parser2 = new Thread(new Runnable)_{ public void run(){ } }); parser1.start(); parser2.start(); parser1.join(); parser1.join(); System.out.print("all parsers finished"); }}
join用於讓當前執行線程等待join線程執行結束。其原理是不停檢查join線程是否存活,若join線程存活則讓當前線程永遠等待。直到join()線程中之後,線程的this.notifyAll()方法會被調用,調用notifyAll()方法是JVM裡實現的。
public class CountDownLatchTest{ static CountDownLatch c = new CountDownLatch(2); public static void main(String[] args) throws InterruptedException{ new Thread(new Runnable(){ System.out.println(1); c.countDown(); System.out.println(2); c.countDown(); }).start(); c.wait(); System.out.println("3"); }}
CountDownLatch的建構函式接受一個int類型的參數作為計數器,若想等待N個點完成,就傳入N。當我們調用CountDownLatch的countDown方法時,N就減1,CountDownLatch的await方法會阻塞當前線程,直到N變成0。由於countDown方法可以用在任何定法,即可以使N個線程。只需要把這個CountDownLatch的引用傳遞到線程裡即可。若某個線程處理的比較慢,可以使用await的重載方法await(long time, TimeUnit unit)。
計數器必須大於0,當計數器等於0時,調用await()方法不會阻塞當前線程。CountDownLatch不可能重新初始化或修改CountDownLatch對象的內部計數器的值。一個線程調用countDown方法happen-before另外一個線程調用await方法。
同步屏障CyclicBarrier
CyclicBarrier的意思是可迴圈使用(Cyclic)的屏障(Barrier)。讓一組線程到達一個屏障時被阻塞,直到最後一個線程到達屏障時,屏障才會開門,所有被屏障攔截的線程才會繼續執行。
CyclicBarrier預設的構造方法是CyclicBarrier(int parties),其參數表示屏障攔截的線程數量,每個線程調用await()方法高速CyclicBarrier我已經到達了屏障,然後當前賢臣被阻塞。
public class CyclicBarrireTest{ static CyclicBarrier c = new CyclicBarrier(2); public static void main(String[] args){ new Thread(new Runnable(){ public void run(){ try{ c.await(); }catch(Exception e){ } System.out.println(1); } }).start(); try{ c.await(); }catch(Exception e){ System.out.println(2); } }}
若把new CyclicBarrier(2)修改成new CyclicBarrire(3),則主線程和子線程會永遠等待,因為沒有第三個線程執行await方法,即沒有第三個線程達到屏障,所以之前到達屏障的兩個線程都不會繼續執行。
CyclicBarrier還提供給了建構函式CyclicBarrier(int parties, Runnable barrierAction)用於線上程到達屏障時先執行barrierAction。
public class CyclicBarrierTest2{ static CyclicBarrier c = new CyclicBarrier(2, new A()); public static void main(String[] args){ new Thread(new Runnable(){ public void run(){ try{ c.await(); }catch(Exception e){} System.out.println(1); } }).start(); try{ c.await(); }catch(Exception e){} System.out.println(2); } static class A implements Runnable{ public void run(){ System.out.println(3); } }}
CyclicBarrier可用於多線程計算資料最後合并計算結果的情境。
public class BankWaterService implements Runnable{ private CyclicBarrier c = new CyclicBarrier(4, this); private Executor executor = Executors.newFixedThreadPool(4, this); private ConcurrentHashMap<String, Integer> sheetBankWaterCount = new ConcurrentHashMap<String, Integer>(); private void count(){ for(int i = 0; i < 4; i++){ executor.execute(new Runnable(){ public void run(){ sheetBankWaterCount.put(Thread.currentThread().getName(), 1); try{ c.await(); }catch(InterruptedException | BrokenBarrierException e){ e.printStackTrace(); } } }); } } public void run(){ int result = 0; for(Entry<String, Integer> sheet : sheetBankWaterCount.entrySet()){ result += sheet.getValue(); } sheetBankWaterCount.put("result". result); System.out.println(result); } public static void main(String[] args){ BankWaterService bankWaterCount = new BankWaterService(); bankWaterCount.count(); } }
public class BankWaterService implements Runnable{ private CyclicBarrier c = new CyclicBarrier(4, this); private Executor executor = Executors.newFixedThreadPool(4, this); private ConcurrentHashMap<String, Integer> sheetBankWaterCount = new ConcurrentHashMap<String, Integer>(); private void count(){ for(int i = 0; i < 4; i++){ executor.execute(new Runnable(){ public void run(){ sheetBankWaterCount.put(Thread.currentThread().getName(), 1); try{ c.await(); }catch(InterruptedException | BrokenBarrierException e){ e.printStackTrace(); } } }); } } public void run(){ int result = 0; for(Entry<String, Integer> sheet : sheetBankWaterCount.entrySet()){ result += sheet.getValue(); } sheetBankWaterCount.put("result". result); System.out.println(result); } public static void main(String[] args){ BankWaterService bankWaterCount = new BankWaterService(); bankWaterCount.count(); } }
CyclicBarrier和CountDownLatch的區別
CountDownLatch的計數器只能使用一次,而CyclicBarrier的計數器可以使用reset()方法重設。CyclicBarrier還提供了getNumberWaiting方法擷取CyclicBarrier阻塞的線程數量。isBroker()方法用來瞭解阻塞的線程是否被中斷。
Semaphore
Semaphore是用來控制同時訪問特定資源的線程數量,它通過協調各個線程,以保證合理的使用公用資源。
Semaphore可以用於做流量控制,特別是公用資源有限的應用情境,如串連資料庫。
public class SemaphoreTest(){ private static final int THREAD_COUNT = 30; private static final ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_COUNT); private static Semaphore s = new Semaphore(10); public static void main(String[] args){ for(int i = 0; i < THREAD_COUNT; i++){ threadPool.executor(new Runnable(){ try{ s.acquire(); System.out.println("save data"); s.release(); }catch(InterruptedException e){} }); } threadPool.shutdown(); }}
Semaphore提供如下方法:
acquire():擷取一個許可證
release():歸還許可證
int avaliablePermits():返回訊號量中當前可用的許可認證
int getQueueLength():返回正在等待擷取許可證的線程書
boolean hasQueuedThreads():是否有線程正在等待擷取許可證
void reducePermits(int reduction):減少reduction個許可證,protected
Collection getQueuedThreads():返回所有等待擷取許可證的線程集合,protected
Exchanger
Exchanger是一個用於線程間協作的工具類。Exchanger用於進行線程間的資料交換。它提供一個同步點,在這個同步點,兩個線程可以交換彼此的資料。這兩個線程通過exchange方法交換資料,若第一個線程先執行exchange()方法,它會一直等待第二個線程也執行exchange()方法,當兩個線程都到達同步點時,這兩個線程可以交換資料,將本線程生產出的資料傳遞給對方。
Exchange可以用於遺傳演算法,遺傳演算法裡需要選出兩個人作為交配對象,這時候交換兩個人資料,並使用交叉規則得出2個交配的結果。同時Exchanger也可以用於校對工作。為了避免錯誤,採用AB崗兩人進行錄入資料到Excel後,系統載入兩個Excel並對兩個Excel資料進行校對。
public class ExchangerTest{ private static final Exchanger<String> exgr = new Exchanger<String>(); private static ExecutorService threadPool = Executors.newFixedThreadPool(2); public static void main(String[] args){ threadPool.execute(new Runnble(){ public void run(){ try{ String A = "banker A"; exgr.exchange(A); }catch(InterruptedException){} } }); threadPool.execute(new Runnable(){ public void run(){ String B = "banker B"; String A = exgr.exchange(B); System.out.println("A equals to B : " + A.equals(B)); } }); threadPool.shutdown(); }}
若兩個線程有一個沒有執行exchange()方法,則會一直等待。此時為避免一直等候,可以調用exchange(V x, long timeout, TimeUnit unit)來設定最大等待時間長度。
Java中的並發工具類