JDK當(dāng)中除了以上的ReentrantLock和Condition之外,還有很多幫助猿友們協(xié)調(diào)線程的工具類。
CountDownLatch(計(jì)數(shù)器閉鎖) 簡單記憶:小明被老師罰站(一個線程await),要等所有同學(xué)走后(設(shè)置的參數(shù)被其他線程countdown()到0)小明才能回家(執(zhí)行await()之后的代碼)。
CyclicBarrier(可循環(huán)使用的屏障)簡單記憶:一場規(guī)定好人數(shù)的百米田徑比賽(new CyclicBarrier(int n)),要等所有人都在起跑線上準(zhǔn)備好(所有線程阻塞在await()方法)之后,才一起出發(fā)(執(zhí)行await()之后的代碼)
Semaphore簡單記憶:廁所里3個坑,10個人一塊來上廁所,每次只能進(jìn)3個,出來一個(release()),再進(jìn)一個(acquire()),想起了一個謎語:“大家排隊(duì)上廁所,打一個城市名?”
Exchanger(交換器)簡單記憶:黑社會a和黑社會b相約到廢棄倉庫交易(final Exchanger<String> exchanger = new Exchanger<String>();),不見不散,誰先到了就等另一人,兩人都到后,a一手交錢exchanger.exchange("錢"),b一手交貨 (exchanger.exchange("貨")),結(jié)果是a得到貨,b得到錢(exchange("")返回值)。
1,CountDownLatch(計(jì)數(shù)器閉鎖)
這個類是為了幫助猿友們方便的實(shí)現(xiàn)一個這樣的場景,就是某一個線程需要等待其它若干個線程完成某件事以后才能繼續(xù)進(jìn)行。比如下面的這個程序
package concurrent;
import java.util.concurrent.CountDownLatch;
/**
* @author zuoxiaolong
*
*/
public class CountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
//只有一個構(gòu)造器,參數(shù)是
final CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
final int number = i + 1;
Runnable runnable = new Runnable() {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
System.out.println("執(zhí)行任務(wù)[" + number + "]");
countDownLatch.countDown();
System.out.println("完成任務(wù)[" + number + "]");
}
};
Thread thread = new Thread(runnable);
thread.start();
}
System.out.println("主線程開始等待...");
countDownLatch.await();
System.out.println("主線程執(zhí)行完畢...");
}
}
這個程序的主線程會等待CountDownLatch進(jìn)行10次countDown方法的調(diào)用才會繼續(xù)執(zhí)行。我們可以從打印的結(jié)果上看出來,盡管有的時候完成任務(wù)的打印會出現(xiàn)在主線程執(zhí)行完畢之后,但這只是因?yàn)閏ountDown已經(jīng)執(zhí)行完畢,主線程的打印語句先一步執(zhí)行而已。
分析一下這個CountDownLatch這個類,CountDownLatch是一個計(jì)數(shù)器閉鎖,主要的功能就是通過await()方法來阻塞住當(dāng)前線程,然后等待計(jì)數(shù)器減少到0了,再喚起這些線程繼續(xù)執(zhí)行。 這個類里主要有兩個方法,一個是向下減計(jì)數(shù)器的方法:countdown(),其實(shí)現(xiàn)的核心代碼如下:
public boolean tryReleaseShared(int releases) {
// 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;
}
}
很簡單,如果取得當(dāng)前的狀態(tài)為0,說明這個鎖已經(jīng)結(jié)束,直接返回false;如果沒有結(jié)束,然后去設(shè)置計(jì)數(shù)器減1,如果compareAndSetState不成功,則繼續(xù)循環(huán)執(zhí)行。 而其中的一直等待計(jì)數(shù)器歸零的方法是await()。
2,CyclicBarrier
這個類是為了幫助猿友們方便的實(shí)現(xiàn)多個線程一起啟動的場景,就像賽跑一樣,只要大家都準(zhǔn)備好了,那就開始一起沖。比如下面這個程序,所有的線程都準(zhǔn)備好了,才會一起開始執(zhí)行。
package concurrent;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* @author zuoxiaolong
*
*/
public class CyclicBarrierTest {
public static void main(String[] args) {
final CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
for (int i = 0; i < 10; i++) {
final int number = i + 1;
Runnable runnable = new Runnable() {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
System.out.println("等待執(zhí)行任務(wù)[" + number + "]");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
} catch (BrokenBarrierException e) {
}
System.out.println("開始執(zhí)行任務(wù)[" + number + "]");
}
};
Thread thread = new Thread(runnable);
thread.start();
}
}
}
3,Semaphore(旗語)
這個類是為了幫助猿友們方便的實(shí)現(xiàn)控制數(shù)量的場景,可以是線程數(shù)量或者任務(wù)數(shù)量等等。來看看下面這段簡單的代碼。
package concurrent;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author zuoxiaolong
*
*/
public class SemaphoreTest {
public static void main(String[] args) throws InterruptedException {
final Semaphore semaphore = new Semaphore(10);
final AtomicInteger number = new AtomicInteger();
for (int i = 0; i < 100; i++) {
Runnable runnable = new Runnable() {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
try {
semaphore.acquire();
number.incrementAndGet();
} catch (InterruptedException e) {}
}
};
Thread thread = new Thread(runnable);
thread.start();
}
Thread.sleep(10000);
System.out.println("共" + number.get() + "個線程獲得到信號");
System.exit(0);
}
}
4,Exchanger
這個類是為了幫助猿友們方便的實(shí)現(xiàn)兩個線程交換數(shù)據(jù)的場景,使用起來非常簡單,看看下面這段代碼。
package concurrent;
import java.util.concurrent.Exchanger;
/**
* @author zuoxiaolong
*
*/
public class ExchangerTest {
public static void main(String[] args) throws InterruptedException {
final Exchanger<String> exchanger = new Exchanger<String>();
Thread thread1 = new Thread(new Runnable() {
public void run() {
try {
System.out.println("線程1等待接受");
String content = exchanger.exchange("thread1");
System.out.println("線程1收到的為:" + content);
} catch (InterruptedException e) {}
}
});
Thread thread2 = new Thread(new Runnable() {
public void run() {
try {
System.out.println("線程2等待接受并沉睡3秒");
Thread.sleep(3000);
String content = exchanger.exchange("thread2");
System.out.println("線程2收到的為:" + content);
} catch (InterruptedException e) {}
}
});
thread1.start();
thread2.start();
}
}
兩個線程在只有一個線程調(diào)用exchange方法的時候調(diào)用方會被掛起,當(dāng)都調(diào)用完畢時,雙方會交換數(shù)據(jù)。在任何一方?jīng)]調(diào)用exchange之前,線程都會處于掛起狀態(tài)。