@Slf4j
public class Test15 {
public static void main(String[] args) {
ThreadPool pool = new ThreadPool(1, 1000, TimeUnit.MILLISECONDS, 1, (taskQueue, task) -> {
// 这里实现自己的拒绝策略
// 死等
// taskQueue.put(task);
// 带超时时间的阻塞添加
taskQueue.offer(task, 1000, TimeUnit.MILLISECONDS);
});
for (int i = 0; i < 3; i++) {
int j = i;
pool.execute(() -> {
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("执行任务:{}", j);
});
}
}
}
@Slf4j
// 自己手写一个简易的线程池
class ThreadPool {
/**
* 什么是线程池呢?
* 顾名思义,线程池就是一个池子里面,有许多的线程。
* 线程池是与工作队列密切相关的,其中在工作对垒中保存了所有等待执行的任务。
* 线程池里的工作线程(Worker)的任务很简单:
* 就是从工作队列中获取一个任务,然后执行,执行完之后,返回线程池,等待下一个任务。
* <p>
* 线程池有啥好处呢?
* 可以重复使用线程池中的线程。避免大量的线程创建和销毁(开销巨大的操作)。从而提升效率。
* 通过适当调整线程池的大小,可以充分的利用 cpu ,让 cpu 一直处于忙碌的状态,不让他闲下来。
* 同时可以避免过多的线程,相互竞争,导致大量的上下文切换,影响效率。
*/
// 存放任务的阻塞队列
private BlockingQueue<Runnable> taskQueue;
// 工作线程集合
private Set<Worker> workers = new HashSet<>();
// 核心线程数量
private int coreSize;
// 获取任务的超时时间
private long timeout;
private TimeUnit timeUnit;
// 拒绝策略
private RejectPolicy<Runnable> rejectPolicy;
public ThreadPool(int coreSize, long timeout, TimeUnit timeUnit, int capacity, RejectPolicy<Runnable> rejectPolicy) {
this.coreSize = coreSize;
this.timeout = timeout;
this.timeUnit = timeUnit;
taskQueue = new BlockingQueue<>(capacity);
this.rejectPolicy = rejectPolicy;
}
// 让线程池来执行任务
public void execute(Runnable task) {
synchronized (workers) {
// 如果工作线程的数量小于 coreSize,则直接新建线程来执行任务
if (workers.size() < coreSize) {
Worker worker = new Worker(task);
log.debug("新增了 worker 线程{}", worker);
workers.add(worker);
worker.start();
} else {
// 如果工作线程的数量 >= coreSize,则将任务加入到任务队列中,让空闲的工作线程去获取执行
// taskQueue.put(task);
// 拒绝策略:【把具体的实现,交给调用者来自己写】
// 1.死等
// 2.带超时时间的等待
// 3.让调用者放弃任务的执行
// 4.让调用者抛出异常
// 5.让调用者自己执行
taskQueue.tryPut(rejectPolicy, task);
}
}
}
class Worker extends Thread {
private Runnable task;
public Worker(Runnable task) {
this.task = task;
}
// 1.当task不为空时,直接执行任务
// 2.当task为空时,去任务队列中去获取任务
@Override
public void run() {
// while (task != null || (task = taskQueue.take()) != null) {
while (task != null || (task = taskQueue.poll(timeout, timeUnit)) != null) {
try {
log.debug("执行任务 {} ", task);
task.run();
} catch (Exception e) {
e.printStackTrace();
} finally {
task = null;
}
}
// 代码运行到这里,说明任务队列中也没有任务了。移除掉这个 worker 线程
synchronized (workers) {
log.debug("移除 worker 工作线程 {}", this);
workers.remove(this);
}
}
}
}
@FunctionalInterface
interface RejectPolicy<T> {
void reject(BlockingQueue<T> taskQueue, T task);
}
@Slf4j
class BlockingQueue<T> {
// 任务队列
private Deque<T> queue = new ArrayDeque<>();
// 锁,防止多个线程同时获取头部的任务时,发生并发安全问题,导致一个任务被重复执行多次;
// 防止多个线程同时向尾部加入新的任务时,发生并发安全问题
private ReentrantLock lock = new ReentrantLock();
// 生产者的条件变量,当队列满的时候,生产者线程就到 fullWaitSet 休息
private Condition fullWaitSet = lock.newCondition();
// 消费者的条件变量,当队列空的时候,消费者线程就到 emptyWaitSet 休息
private Condition emptyWaitSet = lock.newCondition();
// 队列容量
private int capacity;
public BlockingQueue(int capacity) {
this.capacity = capacity;
}
// 加入任务
public void put(T task) {
lock.lock();
try {
while (queue.size() == capacity) {
// 进入 fullWaitSet 休息室等待
try {
log.debug("任务 {} 等待加入任务队列 ...", task);
fullWaitSet.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("任务 {} 加入任务队列", task);
queue.addLast(task);
// 记得唤醒 emptyWaitSet 休息室中等待的线程
emptyWaitSet.signal();
} finally {
lock.unlock();
}
}
// 带超时时间的阻塞添加
public boolean offer(T task, long timeout, TimeUnit timeUnit) {
lock.lock();
try {
long nanos = timeUnit.toNanos(timeout);
while (queue.size() == capacity) {
// 进入 fullWaitSet 休息室等待
try {
log.debug("任务 {} 等待加入任务队列 ...", task);
if (nanos <= 0) {
return false;
}
nanos = fullWaitSet.awaitNanos(nanos);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("任务 {} 加入任务队列", task);
queue.addLast(task);
// 记得唤醒 emptyWaitSet 休息室中等待的线程
emptyWaitSet.signal();
return true;
} finally {
lock.unlock();
}
}
// 一直死等的阻塞获取
public T take() {
lock.lock();
try {
while (queue.size() == 0) {
try {
emptyWaitSet.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
T task = queue.removeFirst();
// 记得唤醒 fullWaitSet 休息室中等待的线程
fullWaitSet.signal();
return task;
} finally {
lock.unlock();
}
}
// 带超时时间的阻塞获取
public T poll(long timeout, TimeUnit timeUnit) {
lock.lock();
try {
long nanos = timeUnit.toNanos(timeout);
while (queue.size() == 0) {
try {
if (nanos <= 0) {
return null;
}
// 返回值是:还要等待的时间。
// 将返回值赋值给 nanos,这样被虚假唤醒之后,还要重头开始等待那么多的时间。
nanos = emptyWaitSet.awaitNanos(nanos);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
T task = queue.removeFirst();
// 记得唤醒 fullWaitSet 休息室中等待的线程
fullWaitSet.signal();
return task;
} 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() == capacity) {
rejectPolicy.reject(this, task);
} else {
// 任务队列还没满
log.debug("加入任务队列 {}", task);
queue.add(task);
emptyWaitSet.signal();;
}
} finally {
lock.unlock();
}
}
}
自己手写一个简易版本的线程池【Java】【详细注解,含思考过程和知识点】
于 2022-12-29 20:44:21 首次发布