1 模拟线程池
1.1 线程池的实现思路
先来看一下Java中的创建一个线程池所需要的参数:

corePoolSize
核心线程数量
maximumPoolSize
线程池最大容量
keepAliveTime
非核心且空闲线程最多存在时间
unit
时间单位
workQueue
工作队列
我们可以向用户提供一个构造器,其中需要的参数就包括corePoolSize,maximumPoolSize等,创建两个队列,一个忙碌线程队列存放正在执行任务的线程,一个空闲线程队列,存放空闲等待任务的线程来简单地完成线程池
其中必须要实现的主要逻辑有:
1,用户提交一个任务,从空闲线程队列中获取空闲线程,为它设置任务开始执行
如果空闲线程队列中没有空闲线程,新建一个线程来执行任务
2,一个线程开始执行任务时,需要被存放到忙碌线程队列
执行完任务后,需要被移动到空闲线程队列
3,空闲线程队列中的线程需要根据keepAliveTime定期回收,销毁已经过期的线程
线程池工作流程图:

1.2 包装用户任务的类 Task
用户提交的任务必然是一个线程,一个Runnable接口的实现类,这里称为用户线程,但是我们不能直接对用户线程进行操作,因为我们想要操作用户线程的工作状态,需要一些参数,而用户线程是没有这些参数的,我们需要将用户线程包装到另一个线程(工作线程)中运行
用户线程
用户execute的任务,即一个Runnable接口的实现类
单纯的执行用户提交的任务
工作线程
用来控制用户线程的工作状态
线程池管理的真正对象,其中有一些参数可以得知用户线程的状态
从而让线程池进行对用户线程的管理
我们创建一个Task类,这个类将用户线程作为成员,且这个类也实现Runnable接口,使这个类的run方法作为工作线程来控制用户线程的工作
package com.coisini.threadPool.core;
/**
* @author 雫
* @date 2021/4/23 - 16:42
* @function 包装用户线程的工作对象
* 线程池实际的控制对象
*/
public class Task implements Runnable {
private Runnable job;
private boolean goon;
private long endTime;
private ThreadPool threadPool;
public Task(ThreadPool threadPool) {
this.threadPool = threadPool;
this.goon = false;
}
public Runnable getJob() {
return job;
}
public void setJob(Runnable job) {
this.job = job;
}
public long getEndTime() {
return endTime;
}
/**
* @Author 雫
* @Description 当没有空闲线程且当前总线程小于maximumPoolSize时
* 新创建一个工作线程并启动执行job
* @Date 2021/4/23 17:46
* @Param []
* @return void
**/
void startTask() {
new Thread(this).start();
this.goon = true;
}
/**
* @Author 雫
* @Description 停止工作线程
* @Date 2021/4/23 17:47
* @Param []
* @return void
**/
void endTask() {
this.goon = false;
}
/**
* @Author 雫
* @Description 由工作线程控制用户线程
* 有job才能执行 每次执行完后重置job变为空闲线程并保存结束时间
* @Date 2021/4/23 17:47
* @Param []
* @return void
**/
@Override
public void run() {
while (this.goon) {
if(this.job != null) {
this.job.run();
this.job = null;
}
/*
* 当terminateThreadPoolNow设置为true时
* 这里还有正在工作线程 需要它们将自己回收
* */
synchronized (this.threadPool.getLock()) {
if(this.threadPool.isTerminateThreadPoolNow()) {
this.threadPool.removeFromBusyTaskQueue(this);
this.goon = false;
}
}
// 记录结束时间并将该Task移到空闲队列中
this.endTime = System.currentTimeMillis();
this.threadPool.fromBusyToFree(this);
}
}
}
1.3 真正管理Task的线程池 ThreadPool
有了Task类后,需要一个真正的线程池管理这些Task,这个线程池的核心在于两个队列,一个用来存储正在执行任务的Task,一个存储空闲的Task,并需要一系列方法更改Task的工作状态
并且需要根据keepAliveTime来回收空闲的过期线程
/**
* @author 雫
* @date 2021/4/23 - 16:52
* @function 管理Task的线程池
* 通过管理Task 操作Task的工作线程来控制用户线程
*/
public class ThreadPool implements Runnable {
private TaskPool taskPool;
private int corePoolSize;
private int maximumPoolSize;
private long keepAliveTime;
private Queue<Task> taskBusyQueue;
private Queue<Task> taskFreeQueue;
private boolean goon;
private Object lock;
/*
* 当线程池被shutdown后 需要清理两个队列中的线程
* 可以直接将taskFreeQueue中的线程清理 但是此时仍有一些线程在taskBusyQueue中工作
* 就需要告知这些工作的线程 当它们结束工作后 需要被回收 terminateThreadPoolNow就作为shutDown后的标志
* */
private boolean terminateThreadPoolNow;
public ThreadPool(TaskPool taskPool) {
this.taskPool = taskPool;
this.corePoolSize = this.taskPool.getCorePoolSize();
this.maximumPoolSize = this.taskPool.getMaximumPoolSize();
this.keepAliveTime = this.taskPool.getKeepAliveTime();
taskBusyQueue = new LinkedBlockingQueue<>();
taskFreeQueue = new LinkedBlockingQueue<>();
this.goon = true;
this.lock = new Object();
this.terminateThreadPoolNow = false;
}
public Object getLock() {
return lock;
}
public boolean isTerminateThreadPoolNow() {
return terminateThreadPoolNow;
}
/**
* @Author 雫
* @Description 真正提交任务执行的方法
* 从taskFreeQueue中获取一个空闲线程 如果存在 则将其添加到taskBusyQueue 并设置job开始执行
* 如果没有且当前线程池中总线程数小于maximumPoolSize 则新建一个线程
* 如果总线程数等于maximumPoolSize 则需要本次提交的任务移到阻塞队列
* @Date 2021/4/23 17:37
* @Param [job]
* @return void
**/
void execute(Runnable job) {
synchronized (lock) {
Task task = this.taskFreeQueue.poll();
if(task == null) {
if(this.taskBusyQueue.size() == this.maximumPoolSize) {
this.taskPool.threadPoolFull(task);
return;
} else {
task = new Task(this);
task.startTask();
}
} else {
this.taskBusyQueue.add(task);
task.setJob(job);
}
}
}
/**
* @Author 雫
* @Description 真正关闭线程池的方法
* 先将terminateThreadPoolNow设置为true 通知正在工作的线程此时线程池已停止 工作执行完后需要被回收
* 再回收空闲线程中的所有线程
* @Date 2021/4/23 17:40
* @Param []
* @return void
**/
void terminateThreadPool() {
synchronized (this.lock) {
this.terminateThreadPoolNow = true;
}
while (!this.taskFreeQueue.isEmpty()) {
Task task = this.taskFreeQueue.poll();
task.endTask();
}
}
/**
* @Author 雫
* @Description 供Task使用 当线程池被关闭后 terminateThreadPoolNow被设置为true
* 就需要直接将这些线程从taskBusyQueue中移除
* @Date 2021/4/23 17:41
* @Param [task]
* @return void
**/
void removeFromBusyTaskQueue(Task task) {
this.taskBusyQueue.remove(task);
}
/**
* @Author 雫
* @Description 供Task使用 正常工作完的线程应该从taskBusyQueue中移到taskFreeQueue中
* 并且此时应该从阻塞队列中获取Task尝试执行
* @Date 2021/4/23 17:42
* @Param [task]
* @return void
**/
void fromBusyToFree(Task task) {
synchronized (this.lock) {
this.taskBusyQueue.remove(task);
this.taskFreeQueue.add(task);
Task waitingTask = this.taskPool.getWaitingTask();
if(waitingTask == null) {
return;
} else {
Task freeTask = this.taskFreeQueue.poll();
freeTask.setJob(waitingTask.getJob());
this.taskBusyQueue.add(freeTask);
}
}
}
/**
* @Author 雫
* @Description 回收空闲过期线程
* 每隔keepAliveTime对taskFreeQueue中的Task进行一次扫描
* 如果当前总线程数大于corePoolSize且taskFreeQueue不为空
* 则需要找到所有过期的空闲线程 将它们回收
* @Date 2021/4/23 17:43
* @Param []
* @return void
**/
@Override
public void run() {
while (this.goon) {
synchronized (this.lock) {
try {
lock.wait(this.keepAliveTime);
recycleTask();
} catch (InterruptedException e) {}
}
}
}
/**
* @Author 雫
* @Description 真正回收的过程
* @Date 2021/4/23 17:45
* @Param []
* @return void
**/
private void recycleTask() {
int currentThreadCount = this.taskBusyQueue.size() + this.taskFreeQueue.size();
if(currentThreadCount > this.corePoolSize && !this.taskFreeQueue.isEmpty()) {
List<Task> tmpList = new ArrayList<>();
long currentTime = System.currentTimeMillis();
for(Task task : this.taskFreeQueue) {
if(currentTime - task.getEndTime() > this.keepAliveTime) {
tmpList.add(task);
}
}
if(tmpList.isEmpty()) {
return;
} else {
for(Task task : tmpList) {
this.taskFreeQueue.remove(task);
}
}
}
}
}
1.4 提供给用户使用的类 TaskPool
为了保证线程池的安全,不建议用户直接使用线程池,创建一个新的类,其中的方法调用线程池中的方法来保证线程池的安全
/**
* @author 雫
* @date 2021/4/23 - 16:54
* @function 提供给用户的工具
* 用户可以通过TaskPool操作ThreadPool
*/
public class TaskPool {
private int corePoolSize;
private int maximumPoolSize;
private long keepAliveTime;
// 由用户操作TaskPool来间接操作ThreadPool
private ThreadPool threadPool;
// 等待执行任务的队列
private Queue<Task> waitingTaskQueue;
public TaskPool(int corePoolSize, int maximumPoolSize, long keepAliveTime) {
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.keepAliveTime = keepAliveTime;
// 创建ThreadPool 并开启ThreadPool的回收线程
this.threadPool = new ThreadPool(this);
new Thread(this.threadPool).start();
this.waitingTaskQueue = new LinkedBlockingQueue<>();
}
public int getCorePoolSize() {
return corePoolSize;
}
public int getMaximumPoolSize() {
return maximumPoolSize;
}
public long getKeepAliveTime() {
return keepAliveTime;
}
/**
* @Author 雫
* @Description 提交任务
* @Date 2021/4/23 17:33
* @Param [job]
* @return void
**/
public void execute(Runnable job) {
this.threadPool.execute(job);
}
/**
* @Author 雫
* @Description 关闭线程池
* @Date 2021/4/23 17:34
* @Param []
* @return void
**/
public void shutDown() {
this.threadPool.terminateThreadPool();
}
/**
* @Author 雫
* @Description 将暂时无法执行的Task存入阻塞队列
* @Date 2021/4/23 17:34
* @Param [task]
* @return void
**/
void threadPoolFull(Task task) {
this.waitingTaskQueue.add(task);
}
/**
* @Author 雫
* @Description 从阻塞队列中取得先到的任务重新执行
* @Date 2021/4/23 17:34
* @Param []
* @return com.coisini.threadPool.core.Task
**/
Task getWaitingTask() {
return this.waitingTaskQueue.poll();
}
}
1.5 小结
简单模拟了线程池的实现,但是耦合程度太高,彼此依赖,阻塞队列中的任务能执行时按照FIFO的方式执行,没有办法为任务添加优先级等操作

1118

被折叠的 条评论
为什么被折叠?



