Java多线程拾遗(七)——实现一个简单的线程池

这篇博客在原有的基础上实现一个建议版本的线程池。

1、准备工作

何为线程池

概念性的东西,我就不班门弄斧了,直接百度百科就介绍的非常全面——线程池(百度百科)。

所谓线程池,顾名思义其实就是一个存放线程的池子,当有任务提交给线程池的时候,池子中的某个线程会主动执行该任务,任务执行完成,线程被线程池回收。如果池子中的线程数量不够,则需要自动扩充新的线程到池子中。任务较少的时候,线程池中的线程能够自动回收,释放资源。

线程池的一些要素

  • 线程数量相关变量

与线程数量相关的控制变量不止有一个,我们这里可以通过三个参数来实现,比如创建线程池时初始的线程数量——init,线程自动扩充时最大的线程数量——max,线程池空闲的时候需要释放一些线程,但是也需要维护一定数量的核心线程数——core

  • 任务队列

在线程池数量扩充到最大的线程数量——max时,新加入的任务会被缓存到任务队列。

  • 任务拒绝策略

如果线程数量达到最大值,同时任务队列也存满,同时还不断的有新的线程请求进入,则需要有相应的拒绝策略来拒绝最新的请求任务线程。

2、简易版本实现

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());
            }));
}

运行日志如下:

Java多线程拾遗(七)——实现一个简单的线程池_第1张图片

主要难点就在于,如何保证线程池中的线程,运行完之后不终止,以及对任务队列的处理。但是这里是一个比较简单的版本,THREAD_QUEUE并没有用上。同时任务队列(TASK_QUEUE)并没有一个阈值,可以无限添加,并没有一个拒绝策略的概念。

3、加入拒绝策略

当线程中的活跃线程数达到一定阈值,需要执行拒绝策略。我们这里引入拒绝策略

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();
    }
}

运行结果
在这里插入图片描述

4、停止自定义线程池

引入了拒绝策略之后,我们需要以一种优雅的方式停止我们的线程池。在外界发送停止指令的时候,我们需要将任务队列中的线程均停止。

//提供给外部的标示,标记线程池是否停止
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!");
}

5、线程数量的动态管理

最后一步,也是比较复杂的一步,加入动态的管理,任务队列中的数量较大时,需要进行线程池活跃数量的动态扩容,如果任务队列中的数量为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();
    }
}

你可能感兴趣的:(多线程)