1. 程式人生 > >java併發之同步器

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 : 廚師