阻塞與喚醒方式的區別
CountDownLatch計數方式
CountDownLatch是減計數。調用await()後線程阻塞。調用countDown()方法後計數減一,當計數爲零時,調用await()的線程被喚醒。
CountDownLatch應用場景爲:
一個或一組線程等待另一組線程完成操作後恢復執行
CountDownLatch例子: 模擬賽跑
開始時一組運動員線程等待begin計數器(初始值爲1),當主線程調用begin.countDown()後begin減1,計數器爲0,這一組運動員線程同時起跑。主線程等待end計數器(初始值爲10)。一個運動員線程到達終點後,調用end.countDown(),end計數器減1。當所有運動員都到達終點後,end計數器爲0,主線程恢復執行。
package CountDownLatch;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class RaceSimulation {
public static void main(String args[]){
//比賽開始的倒數鎖
CountDownLatch begin=new CountDownLatch(1);
//比賽結束的倒數鎖
CountDownLatch end=new CountDownLatch(10);
//十個選手跑步線程
final ExecutorService exec = Executors.newFixedThreadPool(10);
for(int index= 0;index<10;++index){
final int NO=index+1;
Runnable run = new Runnable(){
@Override
public void run() {
try{
//如果計數不爲0,則一直等待
//如果當前計數爲0,此線程立即執行
begin.await();
Thread.sleep((long)(Math.random()*10000));
System.out.println("No."+NO+" arrived");
}catch(InterruptedException e){
e.printStackTrace();
}finally{
//每個選手到達終點時,end就減1
end.countDown();
}
}
};
exec.submit(run);
}
System.out.println("遊戲開始:");
//begin減1,開始遊戲
begin.countDown();
//等待end變爲0,即所有選手到達終點
try {
end.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("遊戲結束");
exec.shutdown();
}
}
CyclicBarrier計數方式
CyclicBarrier是加計數。調用await()後線程阻塞計數器加1,當所有線程都到達屏障被阻塞後,這一組線程才一起恢復執行。
CyclicBarrier的應用場景
一組線程到達一個屏障(即執行CyclicBarrier.await())時被阻塞,直到最後一個線程到達屏障時,屏障纔會開門,所有被屏障攔截的線程纔會繼續幹活。CyclicBarrier默認的構造方法是CyclicBarrier(int parties),其參數表示屏障攔截的線程數量,每個線程調用await方法告訴CyclicBarrier我已經到達了屏障,然後當前線程被阻塞。
CyclicBarier的例子
package cyclicBarrier;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CyclicBarrierDemo {
public static void main(String[] args) {
int N=4;
CyclicBarrier cyclicBarrier=new CyclicBarrier(N,new Runnable() {
@Override
public void run() {
System.out.println("線程"+Thread.currentThread().getName()+"正在執行所有線程到達屏障後執行的操作");
}
});
ExecutorService exec=Executors.newFixedThreadPool(4);
for(int i=0;i<N;++i) {
Runnable r=()->{
try {
System.out.println("線程"+Thread.currentThread().getName()+"正在執行線程的操作");
//用睡眠代替線程的操作
Thread.sleep(new Random().nextInt(1000));
System.out.println("線程"+Thread.currentThread().getName()+"到達屏障");
cyclicBarrier.await();
System.out.println("線程"+Thread.currentThread().getName()+"越過屏障,線程執行完畢");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
};
exec.submit(r);
}
exec.shutdown();
}
}
是否可以重用
CountDownLatch不可以重用
CyclicBarrier可以重用
package cyclicBarrier;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CyclicBarrierDemo {
public static void main(String[] args) {
int N=4;
CyclicBarrier cyclicBarrier=new CyclicBarrier(N,new Runnable() {
@Override
public void run() {
System.out.println("線程"+Thread.currentThread().getName()+"正在執行所有線程到達屏障後執行的操作");
}
});
ExecutorService exec1=Executors.newFixedThreadPool(4);
for(int i=0;i<N;++i) {
Runnable r=()->{
try {
System.out.println("線程"+Thread.currentThread().getName()+"正在執行線程的操作");
//用睡眠代替線程的操作
Thread.sleep(new Random().nextInt(1000));
System.out.println("線程"+Thread.currentThread().getName()+"到達屏障");
cyclicBarrier.await();
System.out.println("線程"+Thread.currentThread().getName()+"越過屏障,線程執行完畢");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
};
exec1.submit(r);
}
exec1.shutdown();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("CyclicBarrier重用");
ExecutorService exec2=Executors.newFixedThreadPool(4);
for(int i=0;i<N;++i) {
Runnable r=()->{
try {
System.out.println("線程"+Thread.currentThread().getName()+"正在執行線程的操作");
//用睡眠代替線程的操作
Thread.sleep(new Random().nextInt(1000));
System.out.println("線程"+Thread.currentThread().getName()+"到達屏障");
cyclicBarrier.await();
System.out.println("線程"+Thread.currentThread().getName()+"越過屏障,線程執行完畢");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
};
exec2.submit(r);
}
exec2.shutdown();
}
}