这篇博客在原有的基础上实现一个建议版本的线程池。
概念性的东西,我就不班门弄斧了,直接百度百科就介绍的非常全面——线程池(百度百科)。
所谓线程池,顾名思义其实就是一个存放线程的池子,当有任务提交给线程池的时候,池子中的某个线程会主动执行该任务,任务执行完成,线程被线程池回收。如果池子中的线程数量不够,则需要自动扩充新的线程到池子中。任务较少的时候,线程池中的线程能够自动回收,释放资源。
与线程数量相关的控制变量不止有一个,我们这里可以通过三个参数来实现,比如创建线程池时初始的线程数量——init,线程自动扩充时最大的线程数量——max,线程池空闲的时候需要释放一些线程,但是也需要维护一定数量的核心线程数——core
在线程池数量扩充到最大的线程数量——max时,新加入的任务会被缓存到任务队列。
如果线程数量达到最大值,同时任务队列也存满,同时还不断的有新的线程请求进入,则需要有相应的拒绝策略来拒绝最新的请求任务线程。
1、先定义一些属性及构造函数
public class SimpleThreadPoolV1 {
//在运行的线程池大小
private final int size;
private final static int DEFAULT_SIZE=10;//线程池的默认大小
//任务队列的大小
private final int queueSize;
private final static int DEFAULT_TASK_QUEUE_SIZE = 2000;//任务队列的默认大小
private static volatile int seq=0;
private final static String THREAD_PREFIX="SIMPLE_THREAD_POOL-";
private final static ThreadGroup GROUP = new ThreadGroup("SELF-POOL-THREADGROUP");
//外部提交的任务线程
private final static LinkedList<Runnable> TASK_QUEUE = new LinkedList<>();
//线程池内部的活跃线程
private final static List<WorkTask> THREAD_QUEUE= new ArrayList<>();
private final DiscardPolicy discardPolicy;//拒绝策略
//默认的拒绝策略
private final static DiscardPolicy DEFAULT_DISCARD_POLICY = ()->{
throw new DiscardException("discard this task.");
};
private int min;//最小线程数
private int max;//最大线程数
private int active;//活跃线程数
//外部提交的任务线程
private final static LinkedList<Runnable> TASK_QUEUE = new LinkedList<>();
//线程池内部的活跃线程队列
private final static List<WorkTask> THREAD_QUEUE= new ArrayList<>();
public SimpleThreadPoolV1() {
this(DEFAULT_SIZE);
}
public SimpleThreadPoolV1(int size) {
this.size = size;
init();
}
/**
* 初始化函数,新建指定个数的线程,放入THREAD_QUEUE
*/
private void init() {
for (int i = 0; i < this.min; i++) {
//创建指定个数的线程,初始化的时候,创建的是min个数
createWorkTask();
}
this.size = min;
this.start();
}
//workTask是我们自定义的Thread,初始化的方式就是启动提交的每一个线程,然后将其放入线程池内部的活跃线程队列。
private void createWorkTask(){
WorkTask task = new WorkTask(GROUP,THREAD_PREFIX+(seq++));
task.start();
THREAD_QUEUE.add(task);
}
}
2、定义几种状态
/**
* 表示当前WorkState的状态
*/
private enum TaskState{
FREE,RUNNING,BLOCKED,TERMINATE;
}
3、封装一个WorkerTask,实质和JDK的Thread一样,只是我们自己封装一个线程类而已
/**
* 对原有的Thread进行封装
* 这里的线程执行完成之后,不能挂掉,需要继续执行后续队列中的任务
*/
private static class WorkTask extends Thread{
private volatile TaskState taskState = TaskState.FREE;
public WorkTask(ThreadGroup threadGroup,String name){
super(threadGroup,name);
}
public TaskState getTaskState(){
return this.taskState;
}
//这里需要重写run方法,让线程执行完指定的任务之后,不要挂掉。
@Override
public void run() {
OUTER:
while(this.taskState!=TaskState.TERMINATE){
//如果线程状态没有终止
//从任务队列中取出任务,然后执行
Runnable runnable;
synchronized (TASK_QUEUE){
while(TASK_QUEUE.isEmpty()){
//没有任务,当前task需要wait
try {
taskState = TaskState.BLOCKED;
TASK_QUEUE.wait();
} catch (InterruptedException e) {
//外部打断的时候,需要退出到while循环之外,不能抛出异常
//这里不能直接抛出异常,如果抛出异常,则线程任务执行完成之后,就会退出,线程就会终止。
break OUTER;
}
}
}
//如果任务队列不是空,则直接取出任务队列中的任务,并调用指定Runnable对象的run方法
//获取任务的时候,不需要在synchronize中
runnable = TASK_QUEUE.removeFirst();
if(null!=runnable){
taskState = TaskState.RUNNING;
runnable.run();
taskState = TaskState.FREE;
}
}
}
}
4、提交任务的逻辑
/**
* 对外提供的提交任务方法
* @param runnable
*/
public void submit(Runnable runnable){
synchronized (TASK_QUEUE){
TASK_QUEUE.addLast(runnable);
TASK_QUEUE.notifyAll();
}
}
5、测试代码
public static void main(String[] args) {
SimpleThreadPoolV1 threadPoolV1 = new SimpleThreadPoolV1();
IntStream.rangeClosed(0,40)
.forEach(i->threadPoolV1.submit(()->{
log.info("the runnable {} be serviced at {}",i,LocalDateTime.now().toString());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("the runnable {} finished at {}",i,LocalDateTime.now().toString());
}));
}
运行日志如下:
主要难点就在于,如何保证线程池中的线程,运行完之后不终止,以及对任务队列的处理。但是这里是一个比较简单的版本,THREAD_QUEUE并没有用上。同时任务队列(TASK_QUEUE)并没有一个阈值,可以无限添加,并没有一个拒绝策略的概念。
当线程中的活跃线程数达到一定阈值,需要执行拒绝策略。我们这里引入拒绝策略
1、拒绝策略的实现
/**
* 拒绝策略的简单实现
*/
public static class DiscardException extends RuntimeException{
public DiscardException(String message) {
super(message);
}
}
/**
* 拒绝策略定义的接口
*/
public interface DiscardPolicy{
void discard() throws DiscardException;
}
2、在提交任务的时候,需要对是否拒绝进行判断
/**
* 对外提供的提交任务方法
* @param runnable
*/
public void submit(Runnable runnable){
synchronized (TASK_QUEUE){
//如果当前任务的数量大于指定任务队列的数量,则直接执行拒绝策略
if(TASK_QUEUE.size()>queueSize){
discardPolicy.discard();
}
TASK_QUEUE.addLast(runnable);
TASK_QUEUE.notifyAll();
}
}
引入了拒绝策略之后,我们需要以一种优雅的方式停止我们的线程池。在外界发送停止指令的时候,我们需要将任务队列中的线程均停止。
//提供给外部的标示,标记线程池是否停止
private volatile boolean isDestory = false;
/**
* 关闭线程池
*/
public void shutdown() throws InterruptedException {
log.info("当前任务队列中任务的个数:{}", TASK_QUEUE.size());
while (!TASK_QUEUE.isEmpty()) {
//如果任务队列不空,需要轮询,等待任务队列执行完成
Thread.sleep(50);
}
int initVal = THREAD_QUEUE.size();
while (initVal > 0) {
for (WorkTask task : THREAD_QUEUE) {
if (task.getTaskState() == TaskState.BLOCKED) {
//如果是阻塞住的,才将其停止
task.interrupt();
task.close();//这里调用close的原因:如果run是在获取任务的时候,是无法响应中断的。
initVal--;
} else {
Thread.sleep(10);
}
}
}
log.info("group account : {}", GROUP.activeCount());
this.isDestory = true;
log.info("Thread pool shutdown!");
}
最后一步,也是比较复杂的一步,加入动态的管理,任务队列中的数量较大时,需要进行线程池活跃数量的动态扩容,如果任务队列中的数量为0时,需要进行线程池活跃数量的自动减少。
这个时候,需要将我们的线程池这个类变成可运行线程的实例,在其中复写run方法,在run方法中完成数量的管控,让我们自定义的线程池作为一个后台线程(有点乱,稍后贴上全部代码会看的更清楚)
/**
* 这里线程池也继承Thread类,复写run方法,然后实现这里面线程个数的管理
* 这里只是起到监控作用,每5秒监控一下目前的task数量,然后根据任务数已经当前的task数量进行相关的调整
*/
@Override
public void run() {
while (!isDestory) {
log.info("thread pool current status,min:{},active:{},max:{},current:{},queuesize:{}", this.min, this.active, this.max, this.size, TASK_QUEUE.size());
if(TASK_QUEUE.size()<=0){
//没任务了,跑路吧
log.info("no task left,return");
isDestory=true;
return;
}
try {
//不用那么频繁的去扫描线程数量,每5秒扫描一次,并进行相关容量的调整
Thread.sleep(5_000L);
int waitTaskCount = TASK_QUEUE.size();//任务队列中缓存的任务数
//线程池扩大
if (waitTaskCount > active && size < active) {
for (int i = size; i < active; i++) {
createWorkTask();
}
log.info("the pool increment to active :{}", active);
size = active;
} else if (waitTaskCount > max && size < max) {
for (int i = size; i < max; i++) {
createWorkTask();
}
log.info("the pool increment to max :{}", max);
size = max;
}
//线程池缩小
synchronized (THREAD_QUEUE) {
if (TASK_QUEUE.isEmpty() && size > active) {
log.info("thread pool start reduce");
int releaseSize = size - active;
for (Iterator<WorkTask> it = THREAD_QUEUE.iterator(); it.hasNext(); ) {
if (releaseSize < 0) {
break;
}
WorkTask task = it.next();
//中止在运行的task
if(TaskState.RUNNING!=task.taskState) {
task.close();
task.interrupt();
it.remove();
releaseSize--;
}
log.info("reducing current running thread is : {}",THREAD_QUEUE.size());
}
size = active;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
本篇博客参考了《Java高并发编程详解》(汪文君 著)中的第8章。无非就是将一些任务存放到TASK_QUEUE中,然后启动的线程存放在THREAD_QUEUE中,而这些线程所做的事情就是去TASK_QUEUE中获取任务,并调用任务指定的逻辑即可。这些总体介绍的有点混乱,对后续学习JUC中的一些问题很有帮助
全部代码如下:
/**
* autor:liman
* createtime:2020/6/22
* comment:简单的线程池——智能化调整线程数量大小
*/
@Slf4j
@Data
public class SimpleThreadPoolV3 extends Thread {
//当前线程池中线程的大小
private int size;
private final static int DEFAULT_SIZE = 10;//线程池的默认大小
//任务队列的大小
private final int queueSize;
private final static int DEFAULT_TASK_QUEUE_SIZE = 2000;//任务队列的默认大小
private static volatile int seq = 0;
private final static String THREAD_PREFIX = "SIMPLE_THREAD_POOL-";
private final static ThreadGroup GROUP = new ThreadGroup("SELF-POOL-THREADGROUP");
//外部提交的任务线程
private final static LinkedList<Runnable> TASK_QUEUE = new LinkedList<>();
//线程池内部的活跃线程
private final static List<WorkTask> THREAD_QUEUE = new ArrayList<>();
private final DiscardPolicy discardPolicy;//拒绝策略
//默认的拒绝策略
private final static DiscardPolicy DEFAULT_DISCARD_POLICY = () -> {
throw new DiscardException("discard this task.");
};
//外部的标示,线程池是否停止
private volatile boolean isDestory = false;
private int min;
private int max;
private int active;
public SimpleThreadPoolV3() {
this(4, 8, 12, DEFAULT_TASK_QUEUE_SIZE, DEFAULT_DISCARD_POLICY);
}
/**
* 提供给外部的构造方法
*
* @param queueSize
* @param discardPolicy
*/
public SimpleThreadPoolV3(int min, int active, int max, int queueSize, DiscardPolicy discardPolicy) {
this.min = min;
this.active = active;
this.max = max;
this.queueSize = queueSize;
this.discardPolicy = discardPolicy;
init();
}
/**
* 初始化函数,新建指定个数的线程,放入THREAD_QUEUE
*/
private void init() {
for (int i = 0; i < this.min; i++) {
//创建指定个数的线程
createWorkTask();
}
this.size = min;
this.start();
}
private void createWorkTask() {
WorkTask task = new WorkTask(GROUP, THREAD_PREFIX + (seq++));
task.start();
THREAD_QUEUE.add(task);
}
/**
* 这里线程池也继承Thread类,复写run方法,然后实现这里面线程个数的管理
* 这里只是起到监控作用,每5秒监控一下目前的task数量,然后根据任务数已经当前的task数量进行相关的调整
*/
@Override
public void run() {
while (!isDestory) {
log.info("thread pool current status,min:{},active:{},max:{},current:{},queuesize:{}", this.min, this.active, this.max, this.size, TASK_QUEUE.size());
if(TASK_QUEUE.size()<=0){
//没任务了,跑路吧
log.info("no task left,return");
isDestory=true;
return;
}
try {
//不用那么频繁的去扫描线程数量,每5秒扫描一次,并进行相关容量的调整
Thread.sleep(5_000L);
int waitTaskCount = TASK_QUEUE.size();//任务队列中缓存的任务数
//线程池扩大
if (waitTaskCount > active && size < active) {
for (int i = size; i < active; i++) {
createWorkTask();
}
log.info("the pool increment to active :{}", active);
size = active;
} else if (waitTaskCount > max && size < max) {
for (int i = size; i < max; i++) {
createWorkTask();
}
log.info("the pool increment to max :{}", max);
size = max;
}
//线程池缩小
synchronized (THREAD_QUEUE) {
if (TASK_QUEUE.isEmpty() && size > active) {
log.info("thread pool start reduce");
int releaseSize = size - active;
for (Iterator<WorkTask> it = THREAD_QUEUE.iterator(); it.hasNext(); ) {
if (releaseSize < 0) {
break;
}
WorkTask task = it.next();
//中止在运行的task
if(TaskState.RUNNING!=task.taskState) {
task.close();
task.interrupt();
it.remove();
releaseSize--;
}
log.info("reducing current running thread is : {}",THREAD_QUEUE.size());
}
size = active;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 对外提供的提交任务方法
*
* @param runnable
*/
public void submit(Runnable runnable) {
//如果线程池已经被销毁,则直接不让提交任务
if (isDestory) {
throw new IllegalStateException("the thread pool already destory");
}
synchronized (TASK_QUEUE) {
//判断数量,决定是否执行拒绝策略
int currentTaskCount = TASK_QUEUE.size();
// log.info("目前的任务数量:{},规定的队列大小:{}", currentTaskCount, queueSize);
if (currentTaskCount > queueSize) {
discardPolicy.discard();
}
TASK_QUEUE.addLast(runnable);
TASK_QUEUE.notifyAll();
}
}
/**
* 拒绝策略
*/
public static class DiscardException extends RuntimeException {
public DiscardException(String message) {
super(message);
}
}
/**
* 拒绝策略定义的接口
*/
public interface DiscardPolicy {
void discard() throws DiscardException;
}
/**
* 关闭线程池
*/
public void shutdown() throws InterruptedException {
log.info("当前任务队列中任务的个数:{}", TASK_QUEUE.size());
while (!TASK_QUEUE.isEmpty()) {
//如果任务队列不空,需要轮询,等待任务队列执行完成
Thread.sleep(50);
}
int initVal = THREAD_QUEUE.size();
while (initVal > 0) {
for (WorkTask task : THREAD_QUEUE) {
if (task.getTaskState() == TaskState.BLOCKED) {
//如果是阻塞住的,才将其停止
task.interrupt();
task.close();//这里调用close的原因:如果run是在获取任务的时候,是无法响应中断的。
initVal--;
} else {
Thread.sleep(10);
}
}
}
log.info("group active account : {}", GROUP.activeCount());
this.isDestory = true;
log.info("Thread pool shutdown!");
}
/**
* 表示当前WorkState的状态
*/
private enum TaskState {
FREE, RUNNING, BLOCKED, TERMINATE;
}
/**
* 对原有的Thread进行封装
* 这里的线程执行完成之后,不能挂掉,需要继续执行后续队列中的任务
*/
private static class WorkTask extends Thread {
private volatile TaskState taskState = TaskState.FREE;
public WorkTask(ThreadGroup threadGroup, String name) {
super(threadGroup, name);
}
public TaskState getTaskState() {
return this.taskState;
}
@Override
public void run() {
OUTER:
while (this.taskState != TaskState.TERMINATE) {
//如果线程状态没有终止
//从任务队列中取出任务,然后执行
Runnable runnable;
synchronized (TASK_QUEUE) {
while (TASK_QUEUE.isEmpty()) {
//没有任务,当前task需要wait
try {
this.taskState = TaskState.BLOCKED;
TASK_QUEUE.wait();
} catch (InterruptedException e) {
//外部打断的时候,需要退出到while循环之外,不能抛出异常
break OUTER;
}
}
runnable = TASK_QUEUE.removeFirst();
}
//获取任务的时候,不需要在synchronize中
if (null != runnable) {
taskState = TaskState.RUNNING;
runnable.run();
taskState = TaskState.FREE;
}
}
}
public void close() {
this.taskState = TaskState.TERMINATE;
}
}
//测试代码
public static void main(String[] args) throws InterruptedException {
SimpleThreadPoolV3 threadPoolV3 = new SimpleThreadPoolV3();
for (int i = 0; i < 40; i++) {
threadPoolV3.submit(() -> {
log.info("the runnable be serviced at {}", LocalDateTime.now().toString());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("the runnable finished at {}", LocalDateTime.now().toString());
});
}
Thread.sleep(10000);
threadPoolV3.shutdown();
}
}