一、CountDowLatch(閉鎖)(倒計數鎖存器)
CountDownLatch類位於java.util.concurrent包下,在完成某些運算時,只有其他所有線程的運算全部完成,當前運算才繼續執行。
利用它可以實現類似計數器的功能。比如有一個任務A,它要等待其他4個任務執行完畢之後才能執行,此時就可以利用CountDownLatch來實現這種功能。
final CountDownLatch latch = new CountDownLatch(2);
new Thread(){
public void run() {
System.out.println("子線程"+Thread.currentThread().getName()+"正在執行");
Thread.sleep(3000);
System.out.println("子線程"+Thread.currentThread().getName()+"執行完畢");
latch.countDown();
};
}.start();
new Thread(){
public void run() {
System.out.println("子線程"+Thread.currentThread().getName()+"正在執行");
Thread.sleep(3000);
System.out.println("子線程"+Thread.currentThread().getName()+"執行完畢");
latch.countDown();
};
}.start();
System.out.println("等待 2 個子線程執行完畢...");
latch.await();
System.out.println("2 個子線程已經執行完畢");
System.out.println("繼續執行主線程");
}
二、Semaphore(信號量-控制同時訪問的線程個數)
信號量可以控制同時訪問的線程個數,通過acquire()獲取一個許可,如果沒有就等待,而通過release()釋放一個許可。
Semaphore類中比較重要的幾個方法:
1)public void acquire():用來獲取一個許可,若無許可能夠獲得,則會一直等待,知道獲得許可。
2)public void acquire(int permits):獲取permits個許可。
3)public void release(){}:釋放許可,注意,在釋放許可之前,必須先獲得許可。
4)public void release(int permits){}:釋放permits個許可。
上面4個方法都會被阻塞,如果想立即得到執行結果,可以使用下面幾個方法:
1)public boolean tryAcquire():嘗試獲取一個許可,若獲取成功,則立即返回true,若獲取失敗,則立即返回false。
2)public boolean tryAcquire(long timeout,TimeUnit unit):嘗試獲取一個許可,若在指定的時間內獲取成功,則立即返回true,否則立即返回false。
3)public boolean tryAcquire(int permits):嘗試獲取permits個許可,若獲取成功,則立即返回true,若獲取失敗,則立即返回false。
4)public boolean tryAcquire(int permits,long timeout,TimeUnit unit):嘗試獲取permits個許可,若在指定的時間內獲取成功,則立即返回true,否則立即返回false。
5)還可以通過availablePermits()方法得到可用的許可數目。
例子:若一個工廠有5臺機器,一臺機器同時只能被一個工人使用,只有使用完了,其他工人才能繼續使用。那麼我們就可以通過Semaphore來實現:
int N = 8; //工人數
Semaphore semaphore = new Semaphore(5); //機器數目
for(int i=0;i<N;i++){
new Worker(i,semaphore).start();
}
static class Worker extends Thread{
private int num;
private Semaphore semaphore;
public Worker(int num,Semaphore semaphore){
this.num = num;
this.semaphore = semaphore;
}
public void run() {
try {
semaphore.acquire();
System.out.println("工人"+this.num+"佔用一個機器在生產...");
Thread.sleep(2000);
System.out.println("工人"+this.num+"釋放出機器");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
三、CyclicBarrier(迴環柵欄-等待至barrier狀態再全部同時執行)
迴環柵欄,通過它可以實現讓一組線程等待至某個狀態之後再全部同時執行,叫做迴環,是因爲當所有等待線程都被釋放以後,CyclicBarrier可以被重用。我們暫且把這種狀態叫做barrier,當調用await()方法之後,線程就處於barrier了。
CyclicBarrier中最重要的方法就是await方法,它有兩個重載版本:
1)public int await():用來掛起當前線程,直至所有線程都到達barrier狀態再同時執行後續任務。
2)public int await(long timeout,TimeUnit unit):讓這些線程等待至一定的時間,如果還有線程沒有到達barrier狀態就直接讓到達barrier的線程執行後續任務。
具體使用如下:
public static void main(String[] args) {
int N = 4;
CyclicBarrier barrier = new CyclicBarrier(N);
for(int i=0;i<N;i++)
new Writer(barrier).start();
}
static class Writer extends Thread{
private CyclicBarrier cyclicBarrier;
public Writer(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
public void run() {
try {
Thread.sleep(5000); //以睡眠來模擬線程需要預定寫入數據操作
System.out.println("線程"+Thread.currentThread().getName()+"寫入數據完畢,等待其他線程寫入完畢");
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
}catch(BrokenBarrierException e){
e.printStackTrace();
}
System.out.println("所有線程寫入完畢,繼續處理其他任務,比如數據操作");
}
}
三者之間的聯繫與區別:
CountDownLatch和CyclicBarrier都能夠實現線程之間的等待,只不過它們側重點不同。
CountDownLatch一般用於某個線程A等待若干個線程執行完任務之後,它才執行;而CyclicBarrier一般用於一組線程互相等待至某個狀態,然後這一組線程再同時執行。
另外,CountDownLatch是不能夠重用的,而CyclicBarrier是可以重用的。
Semaphore其實和鎖有點類似,它一般用於控制對某組資源的訪問權限。
------------------------