java併發之同步器
Java concurrent包中有提供多種同步器,訊號量(Semaphore)、計數栓(CountDownLatch)、迴圈屏障(CyclicBarrier)、交換器(Exchanger)、Phaser
一、 Semaphore同步器
特徵:
1. 經典的訊號量,通過計數器控制對共享資源的訪問
2. Semaphore(int count):建立擁有count個許可證的訊號量
3.acquire()/acquire(int num) : 獲取1/num個許可證
4. release/release(int num) : 釋放1/num個許可證
用以下程式碼模擬該程式設計模型!
執行結果:package com.concurrent.src; import java.util.concurrent.Semaphore; /** * 訊號量程式設計模型(三個客戶去櫃檯辦事,只初始化兩個視窗,當有視窗閒下來才能為第三個客戶服務) * @author admin * */ public class Ademo { public static void main(String[] args) { // 初始化兩個銀行櫃員(視窗) Semaphore semaphore = new Semaphore(2); // 有A,B,C三個客戶 Person p1 = new Person(semaphore,"A"); p1.start(); Person p2 = new Person(semaphore,"B"); p2.start(); Person p3 = new Person(semaphore,"C"); p3.start(); } } class Person extends Thread { private Semaphore semaphore; public Person(Semaphore semaphore,String name) { setName(name); this.semaphore = semaphore; } @Override public void run() { System.out.println(getName() + " is waiting...."); try { // 獲取許可證 semaphore.acquire(); System.out.println(getName() + " is servicing ...."); // 休眠一秒 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(getName() + " is done!"); // 釋放訊號量 semaphore.release(); } }
B is waiting....
B is servicing ....
A is waiting....
A is servicing ....
C is waiting....
B is done!
C is servicing ....
A is done!
C is done!
二、 CountDownLatch同步器
特徵:
1. 必須發生指定數量的事件後才可以繼續執行(比如賽跑比賽,裁判喊出3,2,1之後大家才同時跑)
2. CountDownLatch(int count):必須發生count個數量才可以開啟鎖存器
3. await:等待鎖存器
4. countDown:觸發事件
以下為計數栓程式設計模型:
package com.concurrent.src; import java.util.concurrent.CountDownLatch; /** * 數三個數同時起跑 * @author admin * */ public class Bdemo { public static void main(String[] args) { // 建立一個計算栓,數三個數 CountDownLatch countDownLatch = new CountDownLatch(3); // 有A,B,C個運動員 new Racer(countDownLatch, "A").start(); new Racer(countDownLatch, "B").start(); new Racer(countDownLatch, "C").start(); // 倒計時 for (int i = 0; i < 3; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(3-i); // 倒計時觸發事件 -1 countDownLatch.countDown(); if(i == 2) { System.out.println("start....."); } } } } class Racer extends Thread { private CountDownLatch countDownLatch; public Racer(CountDownLatch countDownLatch,String name) { setName(name); this.countDownLatch = countDownLatch; } @Override public void run() { System.out.println(getName() + " is waiting...."); try { // 等待鎖存器 countDownLatch.await();; for (int i = 0; i < 3; i++) { System.out.println(getName() + " : " + i); } } catch (InterruptedException e) { e.printStackTrace(); } } }
執行結果:
A is waiting....
C is waiting....
B is waiting....
3
2
1
start.....
A : 0
A : 1
A : 2
C : 0
C : 1
C : 2
B : 0
B : 1
B : 2
三、CyclicBarrier同步器
特徵:
1. 適用於只有多個執行緒都到達預定點時才可以繼續執行(比如鬥地主,需要等齊三個人才開始)
2. CyclicBarrier(int num) :等待執行緒的數量
3. CyclicBarrier(int num, Runnable action) :等待執行緒的數量以及所有執行緒到達後的操作
4. await() : 到達臨界點後暫停執行緒
以下程式碼模擬該模型:
package com.concurrent.src;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* 鬥地主:需要等待齊三個玩家遊戲才開始
* @author admin
*
*/
public class Cdemo {
public static void main(String[] args) {
// 建立迴圈屏障,條件滿足時就執行runnable
CyclicBarrier cyclicBarrier = new CyclicBarrier(3, new Runnable() {
@Override
public void run() {
System.out.println("Game start...");
}
});
// 有A,B,C三個玩家
new Player(cyclicBarrier, "A").start();
new Player(cyclicBarrier, "B").start();
new Player(cyclicBarrier, "C").start();
}
}
class Player extends Thread {
private CyclicBarrier cyclicBarrier;
public Player(CyclicBarrier cyclicBarrier,String name) {
setName(name);
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
System.out.println(getName() + " is waiting other player....");
try {
// 屏障處等待
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
執行結果:A is waiting other player....
C is waiting other player....
B is waiting other player....
Game start...
四、 交換器(Exchanger)同步器
此處模擬略!暫時沒用到!
五、Phaser同步器
特徵:
1. 工作方式與CyclicBarrier類似,但是可以定義多個階段
2. Phaser()/Phaser(int num) : 使用指定0/num個party建立Phaser
3. register() : 註冊party
4. arriveAndAdvance() : 到達時等待到所有party到達
5. arriveAndDeregister() : 到達時登出執行緒自己
以下程式碼模擬該同步器:
package com.concurrent.src;
import java.util.concurrent.Phaser;
/**
* 一個一個訂單處理
* @author admin
*
*/
public class Ddemo {
public static void main(String[] args) {
Phaser phaser = new Phaser(1);
System.out.println("starting...");
// 點餐用餐中會包含多個人員服務
new Worker(phaser, "服務員").start();
new Worker(phaser, "打荷員").start();
new Worker(phaser, "廚師").start();
for (int i = 1; i <= 3; i++) {
phaser.arriveAndAwaitAdvance();
System.out.println("Order " + i + " finished");
}
// 登出
phaser.arriveAndDeregister();
System.out.println("Order all done ");
}
}
class Worker extends Thread {
private Phaser phaser;
public Worker(Phaser phaser,String name) {
setName(name);
this.phaser = phaser;
}
@Override
public void run() {
for (int i = 1; i <= 3; i++) {
System.out.println( "current order is : " + i + " : " + getName());
if (i == 3) {
phaser.arriveAndDeregister();
} else {
phaser.arriveAndAwaitAdvance();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
執行結果:
starting...
Order 1 finished
Order 2 finished
Order 3 finished
Order all done
current order is : 1 : 打荷員
current order is : 1 : 服務員
current order is : 1 : 廚師
current order is : 2 : 打荷員
current order is : 2 : 服務員
current order is : 2 : 廚師
current order is : 3 : 打荷員
current order is : 3 : 服務員
current order is : 3 : 廚師