1.CountDownLatch
現在做的這個華為云TaurusDB比賽中,參考的之前參加過阿里的PolarDB大賽的兩個大佬的代碼,發現都有用到CountDownLatch這個類,之前看代碼的時候也看過,但是沒有搞得很明白,自己寫也寫不出來,在此自己先學習一下。
字面理解:CountDownLatch:數量減少的門栓。
創建這樣一個門栓
1
|
CountDownLatch countDownLatch = new CountDownLatch(count); |
參數:count,門栓的計數次數。
在所有線程執行完成之前,調用countDownLatch.await()阻塞主線程。
每當一個線程執行完一個指定動作之后,count就會減少1,當count等于0時,主線程不再阻塞,開始繼續執行下面的代碼,當count大于0時,主線程一直阻塞,等待count變為0。每個線程動作執行結束后,執行countDownLatch.countDown(),這個門栓的count減一。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
int ThreadNum = 16 ; CountDownLatch countDownLatch = new CountDownLatch(ThreadNum); for ( int i = 0 ; i < ThreadNum ; i++){ final int finalI = i; new Thread(() -> { int n = 0 ; System.out.println( "線程應該做的事情" ); while (n < 10 ){ n++; } countDownLatch.countDown(); }).start(); } try { countDownLatch.await(); } catch (InterruptedException e){ logger.infor( "InterruptedException!!" ); } |
2.線程池
其實線程池之前的ipv6的項目里用過,但是也忘記得差不多了,復習一下。
線程在創建和關閉時都需要花費時間,如果為每一個小的任務都創建一個線程,可能創建和銷毀線程所用的時間會多于該線程真實工作所消耗的時間,就會得不償失。除了時間,空間也需要考慮,線程本身也是要占用內存空間的,大量的線程會食用過多的內存資源,可能會造成OOM。另外在回收時,大量的線程會延長GC的停頓時間。
因此在生產環境中使用線程必須對其加以控制和管理
使用線程池之后,創建線程變成了從線程池中獲得空閑的線程,關閉線程變成了歸還線程給線程池。
通過ThreadPoolExecutor可以創建一個線程池,ThreadPoolExecutor實現了Executors接口。
舉個栗子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; public class ThreadPoolTest { public static void main(String[] args) { ThreadPoolExecutor pool = new ThreadPoolExecutor( 10 , 20 , 60 , TimeUnit.SECOUNDS, new ArrayBlockingQueue<Runnable>( 15000 ), new ThreadFactory(){ private AtomicInteger threadId = new AtomicInteger( 0 ); @Override public Thread newThread(Runnable r){ Thread thread = new Thread(r); thread.setDaemon( true ); String prefix = "thread-" ; thread.setName(prefix+threadId.incrementAndGet()); return thread; } }); } } |
這樣就創建了一個線程池。參數依次解釋:
corePoolSize:指定了線程池中線程的數量,線程池中可以有10個存活的線程
maximumPoolSize:指定了線程池中最大的線程數,線程池中最多能有20個存活的線程
keepAliveTime:當線程池中的數量超過corePoolSize時,這些線程在多長時間會被銷毀,60s
unit:keepAliveTime的單位
workQueue:任務隊列,被提交但是沒有被執行的任務存在的地方。他是一個BlockingQueue<Runnable>接口的對象。
threadFactory:線程工廠,你想創建什么樣子的線程
重點說一下workQueue:
根據隊列的功能分類,可以使用以下幾種BlockingQueue接口
補充:Java中CountDownLatch,CyclicBarrier以及Semaphore的使用場景
Java并發包中提供了很多有用的工具類來幫助開發者進行并發編程,今天我就來說說CountDownLatch,CyclicBarrier以及Semaphore這三個的用法和使用場景。
1.CountDownLatch使用場景和用法
CountDownLatch一般是用于某個線程等待其他線程執行完之后,它才能執行。例如一家人在等待爸爸媽媽回家,才能進行晚宴,示例代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
public class CountDownLatchTest { public static void main(String[] args) throws Exception { final CountDownLatch cdl = new CountDownLatch( 2 ); new Thread(){ public void run() { try { System.out.println( "等待老爸回家..." ); Thread.sleep( 5000 ); cdl.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); new Thread(){ public void run() { try { System.out.println( "等待老媽回家..." ); Thread.sleep( 5000 ); cdl.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); cdl.await(); System.out.println( "老爸老媽回來了..." ); System.out.println( "晚宴開始了..." ); } } |
2.CyclicBarrier(柵欄)使用場景和用法
CyclicBarrier一般是一組線程等待至某個狀態,然后這一組線程才能同時執行(感覺跟CountDownLatch有點類似啊,不過仔細想想還是有差別的,感覺容易混淆)。
代碼示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
public class CyclicBarrierTest { public static void main(String[] args) { int count = 3 ; CyclicBarrier cb = new CyclicBarrier(count, new Runnable() { @Override public void run() { //此處所有線程都調用了await方法之后,會走到這里 System.out.println( "所有線程操作完成之后都調用了await方法" ); } }); for ( int i= 0 ;i<count;i++){ new WriteLogHandler(cb).start(); } } static class WriteLogHandler extends Thread{ private CyclicBarrier cb = null ; public WriteLogHandler(CyclicBarrier cb) { this .cb = cb; } @Override public void run() { try { System.out.println( "線程:" + Thread.currentThread().getName() + "開始寫日志" ); Thread.sleep( 2000 ); System.out.println( "線程:" + Thread.currentThread().getName() + "寫日志結束,等待其他線程" ); cb.await(); System.out.println( "所有線程寫日志數據結束,繼續其他操作" ); } catch (Exception e) { e.printStackTrace(); } } } } |
3.Semaphore(信號量)使用場景和用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
Semaphore類似鎖的用法,用于控制對某資源的訪問權限,示例代碼如下: public class SemaphoreTest { public static void main(String[] args) { ExecutorService executor = Executors.newCachedThreadPool(); final Semaphore semaphore = new Semaphore( 5 ); for ( int i= 0 ;i< 10 ;i++){ final int num = i; executor.execute( new Runnable() { @Override public void run() { try { semaphore.acquire(); System.out.println( "正在執行任務" + num); Thread.sleep(( long )Math.random() * 1000 ); System.out.println( "任務" + num + "執行結束" ); semaphore.release(); } catch (Exception e) { e.printStackTrace(); } } }); } executor.shutdown(); } } |
以上就是這三個并發工具類的使用場景和示例,僅為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。如有錯誤或未考慮完全的地方,望不吝賜教。歡迎大家一起交流。
原文鏈接:https://blog.csdn.net/chen_yuxi/article/details/97847857