自定义线程池(详解)
自定义任务队列
package com.hanxu.project42_multithreading.review.biji.ThreadPool;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
// 此处用T的目的,应为有Runnable,Callable两种可能
// 自定义任务队列
@Slf4j
public class BlockingQueue<T> {
// 1.任务队列
// 注意:ArrayDeque不是线程安全的
private Deque<T> queue = new ArrayDeque<>();
// 2.锁----采用可重入锁
private ReentrantLock lock = new ReentrantLock();
// 3.生产者条件变量
private Condition fullWaitSet = lock.newCondition();
// 4.消费者条件变量
private Condition emptyWaitSet = lock.newCondition();
// 5.容量
private Integer capcity;
public BlockingQueue(Integer capcity) {
this.capcity = capcity;
}
// 带超时的阻塞获取
public T poll(long timeout, TimeUnit unit) {
lock.lock();
// 使用try语句,保证lock最后一定会被释放
try {
long nanos = unit.toNanos(timeout);
while (queue.isEmpty()) {
try {
if (nanos <= 0) {
return null;
}
// awaitNanos返回值为剩余时间,小于或等于零的值表示没有剩余时间
nanos = emptyWaitSet.awaitNanos(nanos);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
T t = queue.removeFirst();
// 用signal而不用signalAll的原因:因为一个工作线程只能随机唤醒一个fullWaitSet的线程(表达不太严谨)
fullWaitSet.signal();
return t;
} finally {
lock.unlock();
}
}
// 带超时的阻塞添加
public boolean offer(T task, long timeout, TimeUnit timeUnit) {
lock.lock();
try {
long nanos = timeUnit.toNanos(timeout);
while (queue.size() == capcity) {
try {
if (nanos <= 0) {
return false;
}
log.info("等待加入任务队列{}...", task);
nanos = fullWaitSet.awaitNanos(nanos);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.info("加入任务队列{}", task);
queue.addLast(task);
// 用signal而不用signalAll的原因:因为一个emptyWaitSet的线程只能被一个工作线程来解决(表达不太严谨)
emptyWaitSet.signal();
return true;
} finally {
lock.unlock();
}
}
// 阻塞获取
public T take() {
lock.lock();
try {
while (queue.isEmpty()) {
try {
emptyWaitSet.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
T t = queue.removeFirst();
// 用signal而不用signalAll的原因:因为一个工作线程只能随机唤醒一个fullWaitSet的线程(表达不太严谨)
fullWaitSet.signal();
return t;
} finally {
lock.unlock();
}
}
// 阻塞添加
public void put(T task) {
lock.lock();
try {
while (queue.size() == capcity) {
try {
log.info("等待加入任务队列{}...", task);
fullWaitSet.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.info("加入任务队列{}", task);
queue.addLast(task);
// 用signal而不用signalAll的原因:因为一个emptyWaitSet的线程只能被一个工作线程来解决(表达不太严谨)
emptyWaitSet.signal();
} finally {
lock.unlock();
}
}
public int size() {
lock.lock();
try {
return queue.size();
} finally {
lock.unlock();
}
}
public void tryPut(RejectPolicy<T> rejectPolicy, T task) {
lock.lock();
try {
// 判断队列是否已满
if (queue.size() == capcity) {
// 由用户决定是:
// 1.死等
// 2.带超时等待
// 3.放弃任务执行
// 4.抛出异常
// 5.让用户自己执行任务
rejectPolicy.reject(this, task);
} else {
log.info("加入任务队列{}", task);
queue.addLast(task);
emptyWaitSet.signal();
}
} finally {
lock.unlock();
}
}
}
自定义拒绝策略接口
package com.hanxu.project42_multithreading.review.biji.ThreadPool;
//自定义拒绝策略接口
@FunctionalInterface
interface RejectPolicy<T> {
void reject(BlockingQueue<T> queue, T task);
}
自定义线程池
package com.hanxu.project42_multithreading.review.biji.ThreadPool;
import javafx.concurrent.Worker;
import lombok.extern.slf4j.Slf4j;
import java.util.HashSet;
import java.util.concurrent.TimeUnit;
@Slf4j
// 自定义线程池
// 用泛型T,因为有Runnable和Callable两种情况
public class ThreadPool {
// 任务队列
private BlockingQueue<Runnable> taskQueue;
// 工作线程集合
private HashSet<Worker> workers = new HashSet<>();
// 核心线程数(决定创建几个工作线程)
private Integer coreSize;
// 获取任务的超时时间(工作线程队列没满时采用的)
private Integer timeout;
private TimeUnit timeUnit;
private RejectPolicy<Runnable> rejectPolicy;
public ThreadPool(Integer coreSize, Integer timeout, TimeUnit timeUnit, Integer capcity, RejectPolicy<Runnable> rejectPolicy) {
this.taskQueue = new BlockingQueue<>(capcity);
this.coreSize = coreSize;
this.timeout = timeout;
this.timeUnit = timeUnit;
this.rejectPolicy = rejectPolicy;
}
//执行任务
public void execute(Runnable task) {
synchronized (workers) {
// 当工作线程数没有超过coreSize时,新建worker对象并加入,worker对象开始处理任务队列中的任务
if (workers.size() < coreSize) {
Worker worker = new Worker(task);
log.info("新增工作线程{}", worker);
workers.add(worker);
worker.start();
} else {
// 工作线程队列已经满了,并且任务还没处理完,没有空闲的worker对象来处理任务了,就把任务添加到任务队列中
// 这里可以采用策略模式,由用户来进行选择
// 1.死等
// 2.带超时等待
// 3.放弃任务执行
// 4.抛出异常
// 5.让用户自己执行任务
taskQueue.tryPut(rejectPolicy, task);
}
}
}
class Worker extends Thread {
private Runnable task;
public Worker(Runnable task) {
this.task = task;
}
@Override
public void run() {
// 执行任务
// ①:当task不为空,执行任务
// ②:当task执行完毕,再接着从任务队列获取任务并执行
while (task != null || (task = taskQueue.poll(timeout, timeUnit)) != null) {
try {
log.info("正在执行{}....", task);
task.run();
} catch (Exception e) {
e.printStackTrace();
} finally {
task = null;
}
}
synchronized (workers) {
log.info("工作线程{}被移除了", this);
workers.remove(this);
}
}
}
}
测试
package com.hanxu.project42_multithreading.review.biji.ThreadPool;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;
@Slf4j
public class Main {
public static void main(String[] args) {
ThreadPool threadPool = new ThreadPool(1, 1000, TimeUnit.MILLISECONDS, 1, (queue, task) -> {
// 1.死等
// queue.put(task);
// 2.带超时等待
// queue.offer(task,500,TimeUnit.MILLISECONDS);
// 3.放弃任务执行
// log.debug("放弃{}",task);
// 4.抛出异常
// throw new RuntimeException("任务执行失败"+task);
// 5.让用户自己执行任务
// task.run();
});
for (int i = 0; i < 4; i++) {
int j = i;
threadPool.execute(() -> {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("{}", j);
});
}
}
}
结果
①死等
20:51:44.411 [main] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.ThreadPool - 新增工作线程Thread[Thread-0,5,main]
20:51:44.414 [main] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.BlockingQueue - 加入任务队列com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@69222c14
20:51:44.414 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.ThreadPool - 正在执行com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@3d0e272c....
20:51:44.414 [main] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.BlockingQueue - 等待加入任务队列com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@606d8acf...
20:51:45.421 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.Main - 0
20:51:45.421 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.ThreadPool - 正在执行com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@69222c14....
20:51:45.421 [main] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.BlockingQueue - 加入任务队列com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@606d8acf
20:51:45.421 [main] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.BlockingQueue - 等待加入任务队列com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@470e2030...
20:51:46.429 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.Main - 1
20:51:46.429 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.ThreadPool - 正在执行com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@606d8acf....
20:51:46.429 [main] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.BlockingQueue - 加入任务队列com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@470e2030
20:51:47.432 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.Main - 2
20:51:47.432 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.ThreadPool - 正在执行com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@470e2030....
20:51:48.440 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.Main - 3
20:51:49.447 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.ThreadPool - 工作线程Thread[Thread-0,5,main]被移除了
总计用时5秒
②带超时等待
20:59:27.818 [main] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.ThreadPool - 新增工作线程Thread[Thread-0,5,main]
20:59:27.822 [main] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.BlockingQueue - 加入任务队列com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@69222c14
20:59:27.822 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.ThreadPool - 正在执行com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@60d58424....
20:59:27.822 [main] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.BlockingQueue - 等待加入任务队列com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@606d8acf...
20:59:28.338 [main] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.BlockingQueue - 等待加入任务队列com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@470e2030...
20:59:28.832 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.Main - 0
20:59:28.832 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.ThreadPool - 正在执行com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@69222c14....
20:59:28.832 [main] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.BlockingQueue - 加入任务队列com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@470e2030
20:59:29.835 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.Main - 1
20:59:29.835 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.ThreadPool - 正在执行com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@470e2030....
20:59:30.838 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.Main - 3
20:59:31.842 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.ThreadPool - 工作线程Thread[Thread-0,5,main]被移除了
总计用时4秒,并且第二个任务没有被执行
③放弃任务执行
21:01:31.604 [main] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.BlockingQueue - 加入任务队列com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@69222c14
21:01:31.604 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.ThreadPool - 正在执行com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@255cd55c....
21:01:31.605 [main] DEBUG com.hanxu.project42_multithreading.review.biji.ThreadPool.Main - 放弃com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@606d8acf
21:01:31.605 [main] DEBUG com.hanxu.project42_multithreading.review.biji.ThreadPool.Main - 放弃com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@782830e
21:01:32.606 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.Main - 0
21:01:32.606 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.ThreadPool - 正在执行com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@69222c14....
21:01:33.616 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.Main - 1
21:01:34.619 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.ThreadPool - 工作线程Thread[Thread-0,5,main]被移除了
可以看到第3和4个任务被放弃了
④抛出异常
21:02:45.871 [main] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.ThreadPool - 新增工作线程Thread[Thread-0,5,main]
21:02:45.875 [main] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.BlockingQueue - 加入任务队列com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@69222c14
21:02:45.875 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.ThreadPool - 正在执行com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@255cd55c....
Exception in thread "main" java.lang.RuntimeException: 任务执行失败com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@606d8acf
at com.hanxu.project42_multithreading.review.biji.ThreadPool.Main.lambda$main$0(Main.java:18)
at com.hanxu.project42_multithreading.review.biji.ThreadPool.BlockingQueue.tryPut(BlockingQueue.java:148)
at com.hanxu.project42_multithreading.review.biji.ThreadPool.ThreadPool.execute(ThreadPool.java:54)
at com.hanxu.project42_multithreading.review.biji.ThreadPool.Main.main(Main.java:24)
21:02:46.882 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.Main - 0
21:02:46.882 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.ThreadPool - 正在执行com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@69222c14....
21:02:47.886 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.Main - 1
21:02:48.895 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.ThreadPool - 工作线程Thread[Thread-0,5,main]被移除了
⑤让用户自己执行任务
21:03:48.197 [main] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.ThreadPool - 新增工作线程Thread[Thread-0,5,main]
21:03:48.200 [main] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.BlockingQueue - 加入任务队列com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@69222c14
21:03:48.200 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.ThreadPool - 正在执行com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@3548f505....
21:03:49.212 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.Main - 0
21:03:49.212 [main] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.Main - 2
21:03:50.223 [main] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.Main - 3
21:03:50.223 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.ThreadPool - 正在执行com.hanxu.project42_multithreading.review.biji.ThreadPool.Main$$Lambda$2/212628335@69222c14....
21:03:51.234 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.Main - 1
21:03:52.238 [Thread-0] INFO com.hanxu.project42_multithreading.review.biji.ThreadPool.ThreadPool - 工作线程Thread[Thread-0,5,main]被移除了
可以看到任务3和4是由main线程执行的,1和2是由Thread-0线程执行的