阻塞队列(生产者消费者模式)
生产者一批线程(负责向共享存储区域存放数据),消费者一批线程(负责从共享存储区域取出数据),当共享存储区域数据没有了,消费者需要等一会儿,直到生产者将数据放入共享存储区。当共享存储区域被填充满了,生产者需要等一会儿,等待消费者将数据取出,让共享存储区域有位置可以放!
下面请看代码
// 隐式加锁 使用 synchronized (智慧的锁,根据不同的情况产生不同的锁策略)!
public class BlockQueue {
private long[] array;
private int frontIndex;
private int rearIndex;
private int size;
public BlockQueue(int capacity) {
array = new long[capacity];
frontIndex = 0;
rearIndex = 0;
size = 0;
}
public synchronized void put(long e) throws InterruptedException {
// 是不是满了
while (size == array.length) {
wait();// 作为 P,在等 C
}
// 队列不是满的
array[rearIndex] = e;
rearIndex++;
if (rearIndex == array.length) {
rearIndex = 0;
}
size++;
notifyAll(); //notifyAll() 唤醒等待集合中的所有线程(不区分是消费者线程还是生产者线程)!
}
public synchronized long take() throws InterruptedException {
while (size == 0) {
wait(); // 作为 C,在等 P
}
// 队列一定不是空的
long e = array[frontIndex];
frontIndex++;
if (frontIndex == array.length) {
frontIndex = 0;
}
size--;
notifyAll(); //notifyAll() 唤醒等待集合中的所有线程(不区分是消费者线程还是生产者线程)!
return e;
}
}
// 显式加锁
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class BlockQueue<E> {
private Object[] array;
private int size;
private int from_index;
private int rear_index;
private Lock lock ;
private Condition notEmpty; // 生产者等待集(队列不为空)
private Condition notFull; // 消费者等待集(队列不为满)
public BlockQueue(int capacity){
array = new Object[capacity];
size = 0;
from_index = 0;
rear_index = 0;
lock = new ReentrantLock(true); //公平锁
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
// 当队列为空,消费者等待,生产者工作。队列满了,生产者等待,消费者工作!
public void push(E element) throws InterruptedException {
try{
lock.lock(); // 显示加锁!
while (size == array.length) notFull.await();
array[rear_index] = element;
rear_index++;
if(rear_index == array.length){
rear_index = 0;
}
size++;
notEmpty.signal();
}finally {
lock.unlock();
}
}
public E take() throws InterruptedException {
E ret = null;
try {
lock.lock();
while (size == 0) notEmpty.await();
ret = (E) array[from_index];
from_index++;
if (from_index == array.length) from_index = 0;
size--;
notFull.signal();
} finally {
lock.unlock(); // 确保锁一定会被释放!
return ret;
}
}
}
基于阻塞队列的应用
定时器
能力有限,献丑了!
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
/**
* 实现一个定时器!
*/
public class MyTimer {
// 默认小顶堆!
private BlockingQueue<Task> blockQueue = new PriorityBlockingQueue<>(); // 优先阻塞队列!
private Object newTaskCom = new Object();
static class Task implements Runnable,Comparable<Task> {
private boolean isPermanent; // 是否为循环执行任务!
private long runAt; // 启动任务的时间!
private long delay; // 启动任务的时间间隔!
private Runnable work; // 任务!
public Task(long period, Runnable work) {
this.delay = period;
this.work = work;
long curTime = System.currentTimeMillis();
this.runAt = curTime + period;
}
public Task(boolean isPermanent, long period, Runnable work) {
this.isPermanent = isPermanent;
this.delay = period;
this.work = work;
long curTime = System.currentTimeMillis();
this.runAt = curTime + period;
}
@Override
public void run() {
work.run();
}
@Override
public int compareTo(Task o) {
long sub = this.runAt - o.runAt;
return sub == 0 ? 0 : sub > 0 ? 1 : -1;
}
public long getRunAt() {
return runAt;
}
public void setRunAt(long runAt) {
this.runAt = runAt;
}
public boolean isPermanent() {
return isPermanent;
}
public void setPermanent(boolean permanent) {
isPermanent = permanent;
}
public long getDelay() {
return delay;
}
public void setDelay(long delay) {
this.delay = delay;
}
}
// 消费者
static class TakeRun extends Thread{
private BlockingQueue<Task> blockingQueue;
private Object newTaskCom;
public TakeRun(BlockingQueue<Task> blockingQueue,Object newTaskCom) {
this.blockingQueue = blockingQueue;
this.newTaskCom = newTaskCom;
}
// 从阻塞队列中取出元素,并执行!
@Override
public void run() {
try {
while(true){
Task task = blockingQueue.take();
long cur_time = System.currentTimeMillis();
if(cur_time < task.getRunAt()){ // 未到执行时间!
long gap = task.getRunAt() - cur_time;
synchronized (newTaskCom){ // 未到执行时间!
newTaskCom.wait(gap); // 当有新的元素加入优先队列会提前醒过来!
}
long cur = System.currentTimeMillis();
if(cur >= task.getRunAt()){ // 醒过来的时候,没有比他更先执行的任务或者没来新的任务!
task.run();
// 如果是周期执行!
if(task.isPermanent()){
task.setRunAt(System.currentTimeMillis() + task.getDelay());
blockingQueue.put(task);
}
}else{
// 说明有优先级比取出的更高的!
blockingQueue.put(task);
}
}else{ //已经到了执行时间!
task.run();
if(task.isPermanent()){
task.setRunAt(System.currentTimeMillis() + task.getDelay());
blockingQueue.put(task);
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 开启一个定时器!
public MyTimer() {
TakeRun consumer = new TakeRun(blockQueue,newTaskCom);
consumer.start();
}
// 向定时器中放任务!
public void put(Task task){
try {
blockQueue.put(task);
synchronized (newTaskCom){
newTaskCom.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
MyTimer timer = new MyTimer();
Task task = new Task(true,1000, new Runnable() {
@Override
public void run() {
System.out.println("1s 之后!");
}
});
timer.put(task);
}
}
线程池
话不多说,上代码!
import java.util.concurrent.BlockingQueue;
// 一个正式员工线程要完成的工作
public class CoreJob implements Runnable {
// 需要阻塞队列
private final BlockingQueue<Runnable> workQueue;
private Runnable firstCommand;
CoreJob(BlockingQueue<Runnable> workQueue, Runnable firstCommand) {
this.workQueue = workQueue;
this.firstCommand = firstCommand;
}
@Override
public void run() {
try {
firstCommand.run(); // 优先先把刚提交的任务先做掉了
firstCommand = null; // 这里设置 null 的意思是,不影响 firstCommand 对象被 GC 时的回收
while (!Thread.interrupted()) {
Runnable command = workQueue.take();
command.run();
}
} catch (InterruptedException ignored) {}
}
}
import java.util.concurrent.*;
// 线程池类
public class MyThreadPoolExecutor implements Executor {
// 创建线程的工厂对象
private final ThreadFactory threadFactory;
// 临时工摸鱼的时间上限
private final long keepAliveTime;
private final TimeUnit unit;
// 当前正式员工的数量
private int currentCoreSize;
// 正式员工的数量上限
private final int corePoolSize;
// 当前临时员工的数量
private int currentTemporarySize;
// 临时员工的数量上限
private final int temporaryPoolSize;
// 传递任务的阻塞队列
private final BlockingQueue<Runnable> workQueue;
public MyThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
this.corePoolSize = corePoolSize;
this.temporaryPoolSize = maximumPoolSize - corePoolSize;
this.workQueue = workQueue;
this.threadFactory = threadFactory;
this.keepAliveTime = keepAliveTime;
this.unit = unit;
}
// 向线程池中提交任务
// 目前这个方法不是线程安全版本的
@Override
public void execute(Runnable command) {
// 1. 如果正式员工的数量还低于正式员工的数量上限,则优先创建正式员工处理任务
// 1.1 需要管理,当前正式员工有多少,正式员工的数量上限有多少?
if (currentCoreSize < corePoolSize) {
// 优先创建正式员工进行处理
// 创建一个线程,这个线程中的任务就是不断地取任务-做任务,但是不需要考虑退出的问题
CoreJob job = new CoreJob(workQueue, command);
// Thread thread = new Thread(job); // 不使用工厂创建的线程
Thread thread = threadFactory.newThread(job); // thread 代表的就是正式员工
String name = String.format("正式员工-%d", currentCoreSize);
thread.setName(name);
thread.start();
// 只是两种不同的策略,没有谁是正确的说法
// 1. 把 command 放到队列中;command 的执行次序是在队列已有的任务之后
// 2. 创建正式员工的时候,就把 command 提交给正式员工,让 command 优先执行
// 我们这里采用第二种方案,主要原因就是 java 官方的就是使用的第二种策略
currentCoreSize++;
return;
}
// 走到这里,说明正式员工的数量 == 正式员工的上限了
// 2. 优先把任务放入队列中,如果放入成功,execute 执行结束,否则还需要继续
// 2.1 需要一个阻塞队列
// workQueue.put(command); // 带阻塞的放入,是否满足这里的需求?
// 我们这里希望的是立即得到结果
boolean success = workQueue.offer(command);
if (success == true) {
// 说明放入队列成功
return;
}
// 队列也已经放满了
// 3. 继续判断,临时工的数量有没有到上限,如果没有到达,创建新的临时工来处理
if (currentTemporarySize < temporaryPoolSize) {
// 创建临时工进行处理
TemporaryJob job = new TemporaryJob(keepAliveTime, unit, workQueue, command);
// Thread thread = new Thread(job); // 不使用工厂创建的线程
Thread thread = threadFactory.newThread(job); // thread 代表的就是临时员工
String name = String.format("临时员工-%d", currentTemporarySize);
thread.setName(name);
thread.start();
currentTemporarySize++;
return;
}
// 4. 执行拒绝策略
// 为了实现方便,暂时不考虑其他策略
throw new RejectedExecutionException();
}
}
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
// 一个临时员工线程要完成的工作
public class TemporaryJob implements Runnable {
// 需要阻塞队列
private final BlockingQueue<Runnable> workQueue;
private final long keepAliveTime;
private final TimeUnit unit;
private Runnable firstCommand;
TemporaryJob(long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, Runnable firstCommand) {
this.keepAliveTime = keepAliveTime;
this.unit = unit;
this.workQueue = workQueue;
this.firstCommand = firstCommand;
}
@Override
public void run() {
try {
firstCommand.run(); // 优先先把刚提交的任务先做掉了
firstCommand = null; // 这里设置 null 的意思是,不影响 firstCommand 对象被 GC 时的回收
// 一旦超过一定时间没有任务,临时工是需要退出的
// 1. keepAliveTime + unit 记录起来
// 2. 怎么就知道超过多久没有任务了?如果一定时间内都无法从队列中取出来任务,则认为摸鱼时间够了
while (!Thread.interrupted()) {
// Runnable command = workQueue.take();
Runnable command = workQueue.poll(keepAliveTime, unit);
if (command == null) {
// 说明,没有取到任务
// 说明超时时间已到
// 说明该线程已经 keepAliveTime + unit 时间没有工作了
// 所以,可以退出了
break;
}
command.run();
}
} catch (InterruptedException ignored) {}
}
}
import java.util.concurrent.*;
public class Main {
static class Task implements Runnable {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(15);
} catch (InterruptedException ignored) {}
}
}
static class MyThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
return new Thread(r);
}
}
public static void main(String[] args) {
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(5);
// 同时最多有 15 个任务
// 3 个正式的
// 5 个队列中
// 7 个临时的
// 提交第 16 个任务时就会出现拒绝服务
MyThreadPoolExecutor executor = new MyThreadPoolExecutor(
3, 10, 10, TimeUnit.SECONDS,
workQueue,
new MyThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
// [0, 1, 2] 交给正式员工在处理
// [3, 4, 5, 6, 7] 暂时放在队列中
// [8, 9, 10, 11, 12, 13, 14] 交给临时工处理
// 过了 15s 之后,第一批任务执行结束
// [0, 1, 2]、[8, 9, 10, 11, 12, 13, 14] 执行结束
// 剩下的 [3, 4, 5, 6, 7] 任务具体怎么分配不确定,大概率是交给正式员工执行
// 就算极端情况下,5 个全部给了临时工
// 也至少还有 2 个临时工没有工作
// 再过 10s,至少 2 个,最多 5 个临时工要被解雇
for (int i = 0; i < 15; i++) {
System.out.println("提交任务: " + i);
Task task = new Task();
executor.execute(task);
}
}
}