話不多說,直接擼代碼。
一、CountDownLatch使用示例
package com.alex.latch;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author: alex
* @Date: 2019/3/20
* @Description: 演示CountDownLatch的使用方法。
* 龜兔賽跑的例子,兔子先到達終點,但是比賽繼續,需要等待烏龜也到達終點後,比賽結束。
*/
public class RunningRace {
private static final CountDownLatch latch = new CountDownLatch(2);
private static final ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
/**
* 兔子的線程類
*/
static class RabbitThread implements Runnable {
@Override
public void run() {
try {
System.out.println("兔子線程" + Thread.currentThread().getName() + "開始起跑");
Thread.sleep(1000); //兔子跑的快,所以只用1s就可以到達終點
System.out.println("兔子線程" + Thread.currentThread().getName() + "跑到終點");
latch.countDown(); //兔子到達終點,計數減一
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 烏龜線程
*/
static class TurtleThread implements Runnable {
@Override
public void run() {
try {
System.out.println("烏龜線程" + Thread.currentThread().getName() + "開始起跑");
Thread.sleep(3000); //烏龜跑的慢,所以用3s纔可以到達終點
System.out.println("烏龜線程" + Thread.currentThread().getName() + "跑到終點");
latch.countDown(); //烏龜到達終點,計數減一
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
try {
System.out.println("龜兔賽跑比賽開始...");
fixedThreadPool.submit(new RabbitThread()); //兔子起跑
fixedThreadPool.submit(new TurtleThread()); //烏龜起跑
/**
* 1、要保證兔子和烏龜都到達終點後,纔可以完成比賽。
* 2、要主要使用await方法,而不是wait方法。wait方法是等待線程,在此處是不起作用的。
* 3、await方法是CountDownLatch的阻塞方法。當前線程等到鎖存器計數到零,繼續執行程序。
*/
latch.await();
System.out.println("龜兔賽跑比賽結束....");
fixedThreadPool.shutdownNow();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
運行結果:
龜兔賽跑比賽開始...
兔子線程pool-1-thread-1開始起跑
烏龜線程pool-1-thread-2開始起跑
兔子線程pool-1-thread-1跑到終點
烏龜線程pool-1-thread-2跑到終點
龜兔賽跑比賽結束....
2、CyclicBarrier使用示例
package com.alex.cyclicbarrier;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author: alex
* @Date: 2019/3/20
* @Description: 演示CyclicBarrier用法。利用考試的例子來說明CyclicBarrier的用法。
* 場景描述:
* 模擬有5個學生進行考試,當五個學生都交卷之後,老師開始批卷。
*/
public class ExamDemo {
private static final ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
public static void main(String[] args) {
try {
/**
* CyclicBarrier有兩個構造方法
* public CyclicBarrier(int parties, Runnable barrierAction) {} 當parties個線程執行完成後,隨機獲取一個線程去執行barrierAction動作
* public CyclicBarrier(int parties) {}當parties個線程執行完成後,纔可以進行後續操作。(與CountDownLatch類似)
*/
CyclicBarrier barrier = new CyclicBarrier(5,new Thread(new TeacherThread()));
fixedThreadPool.submit(new StudentThread(1000,barrier));
fixedThreadPool.submit(new StudentThread(2000,barrier));
fixedThreadPool.submit(new StudentThread(3000,barrier));
fixedThreadPool.submit(new StudentThread(4000,barrier));
fixedThreadPool.submit(new StudentThread(5000,barrier));
fixedThreadPool.shutdown();
// for(int i=0;i<5;i++) {
// new Thread(new StudentThread(1000*(i+1),barrier)).start();
// }
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 學生線程,負責答題交卷
*/
static class StudentThread implements Runnable {
private CyclicBarrier cyclicBarrier;
private long answerTime = 1000;
public StudentThread(long answerTime,CyclicBarrier cyclicBarrier) {
this.answerTime = answerTime;
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
try {
System.out.println("學生線程" + Thread.currentThread().getName() + "開始答題...");
Thread.sleep(answerTime); //答題時間
System.out.println("學生線程" + Thread.currentThread().getName() + "結束答題...交卷");
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 老師線程,負責批卷
*/
static class TeacherThread implements Runnable {
@Override
public void run() {
System.out.println("老師線程" + Thread.currentThread().getName() + "開始批卷...");
}
}
}
運行結果:
學生線程pool-1-thread-1開始答題...
學生線程pool-1-thread-2開始答題...
學生線程pool-1-thread-3開始答題...
學生線程pool-1-thread-5開始答題...
學生線程pool-1-thread-4開始答題...
學生線程pool-1-thread-1結束答題...交卷
學生線程pool-1-thread-2結束答題...交卷
學生線程pool-1-thread-3結束答題...交卷
學生線程pool-1-thread-4結束答題...交卷
學生線程pool-1-thread-5結束答題...交卷
老師線程pool-1-thread-5開始批卷...
3、Semaphore使用示例
package com.alex.semaphore;
import java.util.LinkedList;
import java.util.concurrent.Semaphore;
/**
* @author: alex
* @Date: 2019/3/20
* @Description: 演示Semaphore的使用方法,用來控制同時訪問特定資源的線程數量。
* 通過acquire()獲取一個許可,如果沒有就等待,而release()釋放一個許可。
*/
public class DBConnectionPool {
//使用LinkedList存放連接實體,先入先出,取連接從頭獲取,歸還連接放入到隊尾
private LinkedList<DBConnection> connPoolList = null;
//信號量
private Semaphore semaphore = null;
/**
* 構造方法
* @param size 連接池的大小
*/
public DBConnectionPool(int size) {
//初始化連接池
connPoolList = new LinkedList<>();
for (int i = 0; i < size; i++) {
connPoolList.push(new DBConnection("conn_" + i)); //生成連接放入到池中
}
//初始化信號量
semaphore = new Semaphore(size);
}
/**
* 獲取連接
*
* @return 連接實體
*/
public DBConnection getConnection() {
try {
semaphore.acquire(); //獲取一個許可,信號量減一
} catch (InterruptedException e) {
e.printStackTrace();
}
DBConnection conn = null;
synchronized (connPoolList) {
//同步獲取一個連接
conn = connPoolList.removeFirst(); //從集合中刪除並返回第一個元素
}
return conn;
}
/**
* 釋放連接
*
* @param conn 連接
*/
public void releaseConnection(DBConnection conn) {
synchronized (connPoolList) {
connPoolList.addLast(conn); //將指定的元素追加到此集合的末尾。
}
semaphore.release(); //釋放許可,信號量加一
}
}
DBConnection實體
package com.alex.semaphore;
import java.util.LinkedList;
import java.util.concurrent.Semaphore;
/**
* @author: alex
* @Date: 2019/3/20
* @Description: 演示Semaphore的使用方法,用來控制同時訪問特定資源的線程數量。
* 通過acquire()獲取一個許可,如果沒有就等待,而release()釋放一個許可。
*/
public class DBConnectionPool {
//使用LinkedList存放連接實體,先入先出,取連接從頭獲取,歸還連接放入到隊尾
private LinkedList<DBConnection> connPoolList = null;
//信號量
private Semaphore semaphore = null;
/**
* 構造方法
* @param size 連接池的大小
*/
public DBConnectionPool(int size) {
//初始化連接池
connPoolList = new LinkedList<>();
for (int i = 0; i < size; i++) {
connPoolList.push(new DBConnection("conn_" + i)); //生成連接放入到池中
}
//初始化信號量
semaphore = new Semaphore(size);
}
/**
* 獲取連接
*
* @return 連接實體
*/
public DBConnection getConnection() {
try {
semaphore.acquire(); //獲取一個許可,信號量減一
} catch (InterruptedException e) {
e.printStackTrace();
}
DBConnection conn = null;
synchronized (connPoolList) {
//同步獲取一個連接
conn = connPoolList.removeFirst(); //從集合中刪除並返回第一個元素
}
return conn;
}
/**
* 釋放連接
*
* @param conn 連接
*/
public void releaseConnection(DBConnection conn) {
synchronized (connPoolList) {
connPoolList.addLast(conn); //將指定的元素追加到此集合的末尾。
}
semaphore.release(); //釋放許可,信號量加一
}
}
測試類:
package com.alex.semaphore;
/**
* @author: alex
* @Date: 2019/3/20
* @Description: 模擬場景:數據庫連接池的大小爲2,當有3個線程同時請求獲取連接時,有2個請求可以獲得連接池,1個請求處於等待狀態。
* 當有連接歸還後,處於等待的請求即可獲得連接。
*/
public class DBConnectionTest {
public static void main(String[] args) {
DBConnectionPool pool = new DBConnectionPool(2);
/**
* 開啓3個線程請求分配連接
*/
for (int i = 0; i < 3; i++) {
new Thread() {
public void run() {
try {
System.out.println("線程" + Thread.currentThread().getName() + "準備獲取數據庫連接...");
DBConnection connection = pool.getConnection();
System.out.println("線程" + Thread.currentThread().getName() + "拿到數據庫連接:" + connection.getId());
//模擬處理業務邏輯,每個連接使用的時間不相等
int index = Integer.valueOf(connection.getId().split("_")[1]);
Thread.sleep(1000 * index);
pool.releaseConnection(connection);
System.out.println("線程" + Thread.currentThread().getName() + "歸還數據庫連接...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
}
}
運行結果:
線程Thread-0準備獲取數據庫連接...
線程Thread-1準備獲取數據庫連接...
線程Thread-0拿到數據庫連接:conn_1
線程Thread-2準備獲取數據庫連接...
線程Thread-1拿到數據庫連接:conn_0
線程Thread-1歸還數據庫連接...
線程Thread-2拿到數據庫連接:conn_0
線程Thread-2歸還數據庫連接...
線程Thread-0歸還數據庫連接...
都是一些工具類的簡單使用。配合api就可以看懂,不做過多的解釋。