话不多说,直接撸代码。
一、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就可以看懂,不做过多的解释。