前言
這三個(gè)類都是在java1.5的時(shí)候由Doug Lea大神添加于java.util.concurrent环揽,這三個(gè)輔助類都基于AQS同步器框架實(shí)現(xiàn),下面我們簡單介紹下它們的簡單使用
CountDownLatch
CountDownLatch類似是一個(gè)計(jì)數(shù)器庵佣,他可以實(shí)現(xiàn)需要所有任務(wù)都執(zhí)行完畢才可以執(zhí)行接下來的任務(wù)歉胶,日常場(chǎng)景中我們可以使用他來做并行分布運(yùn)算,借用多核cpu對(duì)數(shù)據(jù)分別進(jìn)行計(jì)算巴粪,然后再匯總通今,也可以實(shí)現(xiàn)在加載某些東西前初始化一些信息粥谬。
主要方法
public CountDownLatch(int count);//構(gòu)造函數(shù)
public void countDown()辫塌;//計(jì)數(shù)器-1
public void await() throws InterruptedException漏策;//掛起
public boolean await(long timeout, TimeUnit unit);//與await()類似,這里可以指定時(shí)間臼氨,達(dá)到時(shí)間如果計(jì)數(shù)器沒歸0也可以執(zhí)行下面的東西
簡單例子
private static void countDownLatchApply() throws Exception {
//初始化2個(gè)
CountDownLatch countDownLatch = new CountDownLatch(2);
new Thread(() -> {
try {
System.out.println("我是線程" + Thread.currentThread().getName() + "我執(zhí)行在"
+LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss")));
//模擬處理耗時(shí)
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
}).start();
new Thread(() -> {
try {
System.out.println("我是線程" + Thread.currentThread().getName() + "我執(zhí)行在"+
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss")));
//模擬處理耗時(shí)
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
}).start();
countDownLatch.await();
new Thread(() -> {
System.out.println("我是線程" + Thread.currentThread().getName() + "我需要前面兩個(gè)執(zhí)行完我才可以執(zhí)行掺喻,我執(zhí)行在"+
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss")));
}).start();
}
執(zhí)行結(jié)果
我是線程Thread-1我執(zhí)行在2019/05/15 08:05:36
我是線程Thread-0我執(zhí)行在2019/05/15 08:05:36
我是線程Thread-2我需要前面兩個(gè)執(zhí)行完我才可以執(zhí)行,我執(zhí)行在2019/05/15 08:05:38
我們會(huì)看到線程2是需要在線程0和1執(zhí)行玩后再執(zhí)行的储矩,我特意讓線程休眠了2秒感耙,后面的時(shí)間也印證了線程2是在線程0和線程1后2秒執(zhí)行的
CyclicBarrier
回環(huán)柵欄,他可以使線程全部到達(dá)一個(gè)同步點(diǎn)后持隧,再一起執(zhí)行下面的動(dòng)作抑月,他是可重用的,等線程到達(dá)同步點(diǎn)舆蝴,這個(gè)線程是可以被做其他使用的洁仗,我們姑且叫這個(gè)狀態(tài)為可重用態(tài),當(dāng)調(diào)用await(),線程就為可重用態(tài)
主要方法
public CyclicBarrier(int parties);//構(gòu)造方法
public CyclicBarrier(int parties, Runnable barrierAction);//構(gòu)造方法,可實(shí)現(xiàn)更復(fù)雜的動(dòng)作
public int await() throws InterruptedException, BrokenBarrierException;//掛起
public int await(long timeout, TimeUnit unit);//帶時(shí)間,到期可執(zhí)行下面操作
簡單例子
private static void cyclicBarrierApply() {
CyclicBarrier cyclicBarrier=new CyclicBarrier(4, new Runnable() {
@Override
public void run() {
System.out.println("我是線程"+Thread.currentThread().getName()+",老板,我按照你的要求在他們向你匯報(bào)前檢查了他們4個(gè)的工作伞访,他們一會(huì)會(huì)親自向你匯報(bào)");
}
});
for (int i = 0; i <4 ; i++) {
new Thread(() ->{
try {
Thread.sleep(2000L);
System.out.println("我是線程"+Thread.currentThread().getName()+",我的工作做完了,等其他線程工作好"
+"袜爪,我們一起去向老板匯報(bào)");
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}finally {
System.out.println("老板豁延,我來向你匯報(bào)工作了");
}
}).start();
}
}
執(zhí)行結(jié)果
我是線程Thread-0,我的工作做完了,等其他線程工作好,我們一起去向老板匯報(bào)
我是線程Thread-1,我的工作做完了,等其他線程工作好湾蔓,我們一起去向老板匯報(bào)
我是線程Thread-3,我的工作做完了咸包,等其他線程工作好烂瘫,我們一起去向老板匯報(bào)
我是線程Thread-2,我的工作做完了,等其他線程工作好怜校,我們一起去向老板匯報(bào)
我是線程Thread-2,老板,我按照你的要求在他們向你匯報(bào)前檢查了他們4個(gè)的工作,他們一會(huì)會(huì)親自向你匯報(bào)
我是線程Thread-2老板键科,我來向你匯報(bào)工作了
我是線程Thread-3老板,我來向你匯報(bào)工作了
我是線程Thread-1老板漩怎,我來向你匯報(bào)工作了
我是線程Thread-0老板勋颖,我來向你匯報(bào)工作了
我們可以看到所有線程最開始都在完成自己的工作,并等待其他線程一起向“老板”匯報(bào)勋锤,線程完成工作了饭玲,變?yōu)榱丝芍赜脩B(tài),這個(gè)時(shí)候線程2被重用叁执,他被“老板”安排在四個(gè)線程匯報(bào)工作前先檢查他們的工作茄厘,也就是第二個(gè)構(gòu)造方法的運(yùn)用,最后四個(gè)線程一起去向“老板”匯報(bào)工作谈宛。
CountDownLatch與CyclicBarrier的比較
他們的功能有一些類似次哈,CountDownLatch是所有線程都到達(dá)一個(gè)點(diǎn)才能執(zhí)行下面的動(dòng)作,而CyclicBarrier是所有線程都到達(dá)一個(gè)點(diǎn)再一起執(zhí)行下面的動(dòng)作,CountDownLatch不可被重用,CyclicBarrier可以被重用
Semaphore
信號(hào)量泡徙,它可用于對(duì)資源進(jìn)行有效的控制该肴,獲取到許可就可以使用,使用完許可主動(dòng)釋放掉障陶,獲取不到就需要等到有許可可以使用
主要方法
public Semaphore(int permits) //參數(shù)表示許可數(shù)目滋恬,同時(shí)可以允許多少線程進(jìn)行訪問
public Semaphore(int permits, boolean fair) //fair表示是否是公平的,等待時(shí)間越久的越先獲得許可
public void acquire() throws InterruptedException ; //獲取一個(gè)許可 會(huì)造成阻塞
public void acquire(int permits) throws InterruptedException ; //獲取permits個(gè)許可 會(huì)造成阻塞
public void release() ; //釋放一個(gè)許可 會(huì)造成阻塞
public void release(int permits) ; //釋放permits個(gè)許可 會(huì)造成阻塞
//下面四個(gè)方法與上面的一樣抱究,但是下面會(huì)立即獲得結(jié)果恢氯,不會(huì)阻塞,操作失敗就返回false
public boolean tryAcquire();
public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException
public boolean tryAcquire(int permits)
public boolean tryAcquire(int permits, long timeout, TimeUnit unit)
簡單例子
private static void semaphoreApply() throws Exception {
//倉庫管理員
Semaphore semaphore = new Semaphore(4);
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
//向倉庫管理員要一把鑰匙
semaphore.acquire();
System.out.println("我是線程" + Thread.currentThread().getName() + "我成功申請(qǐng)到了一把鑰匙");
Thread.sleep(3000L);
System.out.println("我是線程" + Thread.currentThread().getName() + "我使用好了鑰匙");
//給管理員說我用好了
semaphore.release();
System.out.println("我是線程" + Thread.currentThread().getName() + "我已經(jīng)將鑰匙還回去了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
運(yùn)行結(jié)果
我是線程Thread-0我成功申請(qǐng)到了一把鑰匙
我是線程Thread-2我成功申請(qǐng)到了一把鑰匙
我是線程Thread-1我成功申請(qǐng)到了一把鑰匙
我是線程Thread-3我成功申請(qǐng)到了一把鑰匙
我是線程Thread-2我使用好了鑰匙
我是線程Thread-1我使用好了鑰匙
我是線程Thread-4我成功申請(qǐng)到了一把鑰匙
我是線程Thread-6我成功申請(qǐng)到了一把鑰匙
我是線程Thread-0我使用好了鑰匙
我是線程Thread-2我已經(jīng)將鑰匙還回去了
我是線程Thread-1我已經(jīng)將鑰匙還回去了
我是線程Thread-5我成功申請(qǐng)到了一把鑰匙
我是線程Thread-0我已經(jīng)將鑰匙還回去了
我是線程Thread-3我使用好了鑰匙
我是線程Thread-3我已經(jīng)將鑰匙還回去了
我是線程Thread-7我成功申請(qǐng)到了一把鑰匙
我是線程Thread-5我使用好了鑰匙
我是線程Thread-5我已經(jīng)將鑰匙還回去了
我是線程Thread-6我使用好了鑰匙
我是線程Thread-7我使用好了鑰匙
我是線程Thread-9我成功申請(qǐng)到了一把鑰匙
我是線程Thread-4我使用好了鑰匙
我是線程Thread-7我已經(jīng)將鑰匙還回去了
我是線程Thread-6我已經(jīng)將鑰匙還回去了
我是線程Thread-8我成功申請(qǐng)到了一把鑰匙
我是線程Thread-4我已經(jīng)將鑰匙還回去了
我是線程Thread-9我使用好了鑰匙
我是線程Thread-9我已經(jīng)將鑰匙還回去了
我是線程Thread-8我使用好了鑰匙
我是線程Thread-8我已經(jīng)將鑰匙還回去了
我們可以看到鼓寺,最開始只有4個(gè)獲取到了許可勋拟,我在這里將線程休眠3秒,模擬耗時(shí)妈候,在這期間沒有其他線程獲取到許可敢靡,并且同時(shí)也只能有四個(gè)在運(yùn)行,每下一個(gè)獲取到許可都必須是有線程釋放許可,
參考資料:《java編程思想》