文章目錄
1.JUC
JUC就是java.util .concurrent工具包的簡稱。這是一個處理線程的工具包,JDK 1.5開始出現的。
分類說明JUC包常用類有哪些
1.1 CountDownLatch
1.1.1 介紹
CountDownLatch是一個同步工具類,用來
協調多個線程之間的同步
,或者說起到線程之間的通信(而不是用作互斥的作用
)。
CountDownLatch能夠使一個線程
在等待另外一些線程
完成各自工作之後,再繼續執行。使用一個計數器進行實現。計數器初始值爲線程的數量。當每一個線程完成自己任務後,計數器的值就會減一。當計數器的值爲0時,表示所有的線程都已經完成一些任務,然後在CountDownLatch上等待的線程就可以恢復執行接下來的任務。
1.1.2 舉例
比如老師檢查學生作業,有三個學生,這三個學生需要都做完作業後,老師才一起進行檢查。這裏,我們把每一個學生對應一個線程,老師對應一個主線程,當主線程要進行檢查操作時,必須等待三個線程都處理完了,纔開始主線程自己的步驟。
學生類:
/**
* 學生
*/
public class Student implements Runnable{
//線程內共享倒計數門閥
private CountDownLatch downLatch;
private String name;
public Student(CountDownLatch downLatch, String name) {
this.downLatch = downLatch;
this.name = name;
}
public void run() {
System.out.printf("學生%s 開始做作業!\n",name);
try {
TimeUnit.SECONDS.sleep(new Random().nextInt(10));
} catch (Exception e) {
e.printStackTrace();
}
System.out.printf("學生%s 作業結束!\n",name);
//當前線程執行完後,記得調用門閥
downLatch.countDown();
}
}
老師類:
/**
* 老師
*/
public class Teacher {
public static void main(String[] args) {
//創建倒計數(CountDown)門閂(Latch)
CountDownLatch latch = new CountDownLatch(3);
ExecutorService executor = Executors.newCachedThreadPool();
executor.execute(new Student(latch,"A"));
executor.execute(new Student(latch,"B"));
executor.execute(new Student(latch,"C"));
//線程池用完了記得關閉,不然資源一直佔用,比如main方法會一直在執行
executor.shutdown();
System.out.printf("老師%s 等待學生作業!\n",Thread.currentThread().getName());
try {
//等待三個線程全部執行完
latch.await();
} catch (Exception e) {
e.printStackTrace();
}
System.out.printf("老師%s 學生作業全部做完,開始檢查!\n",Thread.currentThread().getName());
}
}
打印:
1.1.3 注意
1.各個線程中最好有異常處理,不要讓一個線程阻塞了導致請他線程也跟着阻塞
1.2 CyclicBarrier
1.2.1 介紹
CyclicBarrier字面意思是“可重複使用的柵欄”,CyclicBarrier 相比 CountDownLatch 來說,要簡單很多,其源碼沒有什麼高深的地方,它是 ReentrantLock 和 Condition 的組合使用。
在CyclicBarrier類的內部有一個計數器,每個線程在到達屏障點的時候都會調用await方法將自己阻塞,此時計數器會減1,當計數器減爲0的時候所有因調用await方法而被阻塞的線程將被喚醒。這就是實現一組線程相互等待的原理
1.2.2 舉例
例如100米賽跑,有3個運動員參加,教練需要看到3個運動員都準備好了,然後才鳴槍,三個運動員同時起跑。現在三個運動員對應三個線程,當教練(主線程)執行100米比賽時,通過查看cyclicBarrier的計數是否爲0,爲0則所有線程開始都被喚醒開始同時進行。
遠動員:
/**
* 運動員
*/
public class Player implements Runnable{
private CyclicBarrier cyclicBarrier;
private String name;
public Player(CyclicBarrier cyclicBarrier, String name) {
this.cyclicBarrier = cyclicBarrier;
this.name = name;
}
public void run() {
try {
//睡眠阻塞
cyclicBarrier.await();
System.out.printf("運動員%s 開始起跑!時間:%s\n",name,getDateTime());
TimeUnit.SECONDS.sleep(new Random().nextInt(10));
} catch (Exception e) {
e.printStackTrace();
}
System.out.printf("運動員%s 100米跑道終點!時間:%s\n",name,getDateTime());
}
public String getDateTime(){
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(d);
}
}
教練:
/**
* 教練
*/
public class Trainer {
public static void main(String[] args) {
int threadCount = 3;
//創建柵欄
CyclicBarrier cyclicBarrier = new CyclicBarrier(threadCount);
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 1; i <= threadCount; i++) {
executor.execute(new Player(cyclicBarrier,"A"+i));
System.out.printf("運動員%s準備就緒,等待其他玩家!\n","A"+i);
}
executor.shutdown();
}
}
打印:
1.2.3 注意
1.CountDownLatch 是一次性的,CyclicBarrier 是可循環利用的
2.CountDownLatch 參與的線程的職責是不一樣的,有的在倒計時,有的在等待倒計時結束。CyclicBarrier 參與的線程職責是一樣的。
3CountDownLatch是線程組之間的等待,即一個(或多個)線程等待N個線程完成某件事情之後再執行;而CyclicBarrier則是線程組內的等待,即每個線程相互等待,即N個線程都被攔截之後,然後依次執行。
4.CountDownLatch是減計數方式,而CyclicBarrier是加計數方式。
5.CountDownLatch計數爲0無法重置,而CyclicBarrier計數達到初始值,則可以重置。
1.3 Semaphore
1.3.1 介紹
Semaphore用於限制可以訪問某些資源(物理或邏輯的)的線程數目,他維護了一個許可證集合,有多少資源需要限制就維護多少許可證集合,假如這裏有N個資源,那就對應於N個許可證,同一時刻也只能有N個線程訪問。一個線程獲取許可證就調用acquire方法,用完了釋放資源就調用release方法。
Semaphore 類是一個計數信號量,必須由獲取它的線程釋放, 通常用於限制可以訪問某些資源(物理或邏輯的)線程數目
1.3.2 舉例
比如一個停車場有三個車位,有6輛車進入,那麼當停滿3輛車後,停車場是不能再進去停車場的,必須其中一個車出來,空出一個車位後,後面的車才能再次進入。車位就相當於Semaphore信號量,車輛可看做一個線程,程序中只允許3個線程進行執行,不管後面線程有多少,當一個線程執行完了,那麼後面線程纔能有一個開始執行。
車輛類:
public class Car implements Runnable{
private Semaphore semaphore;
private String name;
public Car(Semaphore semaphore, String name) {
this.semaphore = semaphore;
this.name = name;
}
public void run() {
try {
//查看是否還有車位
semaphore.acquire();
System.out.printf("車主%s 有車位,開車進入!\n",name);
TimeUnit.SECONDS.sleep(new Random().nextInt(10));
System.out.printf("車主%s 離開停車場!\n",name);
//釋放佔用的車位
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
停車場類:
/**
* 停車場
*/
public class Park {
public static void main(String[] args) {
//信號量,即3個車位
Semaphore ParkingLot = new Semaphore(3);
ExecutorService executor = Executors.newCachedThreadPool();
//6輛車開車進入
for (int i = 1; i <= 6; i++) {
executor.execute(new Car(ParkingLot,"A"+i));
}
executor.shutdown();
}
}
打印:
1.3.3 注意
1.4 Exchanger
1.4.1 介紹
Exchanger併發輔助類,允許在併發任務之間交換數據。具體來說Exchanger類在兩個線程之間定義同步點。當兩個線程到達同步點時,它們交換數據結構。
Exchanger 是 JDK 1.5 開始提供的一個用於兩個工作線程之間交換數據的封裝工具類,簡單說就是一個線程在完成一定的事務後想與另一個線程交換數據,則第一個先拿出數據的線程會一直等待第二個線程,直到第二個線程拿着數據到來時才能彼此交換對應數據。
平時沒怎麼用到
1.4.2 舉例
兩個生產者線程,當調用Exchanger時,會互相傳遞數據
public class ProducerA extends Thread{
private Exchanger<Integer> exchanger;
private static int data = 2;
public ProducerA(Exchanger<Integer> exchanger, String name) {
super.setName(name);
this.exchanger = exchanger;
}
@Override
public void run() {
System.out.println(getName()+" 交換前:" + data);
try {
TimeUnit.SECONDS.sleep(2);
data = exchanger.exchange(data);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+" 交換後:" + data);
}
}
public class ProducerB extends Thread{
private Exchanger<Integer> exchanger;
private static int data = 0;
public ProducerB(Exchanger<Integer> exchanger, String name) {
this.exchanger = exchanger;
super.setName(name);
}
@Override
public void run() {
System.out.println(getName()+" 交換前:" + data);
try {
TimeUnit.SECONDS.sleep(1);
data = exchanger.exchange(data);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+" 交換後:" + data);
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
Exchanger<Integer> exchanger = new Exchanger<Integer>();
new ProducerA(exchanger,"AA").start();
new ProducerB(exchanger,"BB").start();
TimeUnit.SECONDS.sleep(5);
}
}
參考
Java Exchanger 必知必會
Semaphore的工作原理及實例
semaphore/
Semaphore 相關整理
Java併發編程之CyclicBarrier詳解
CountDownLatch的理解和使用