Java Web 基础篇 L7 模拟线程池

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的方式执行,没有办法为任务添加优先级等操作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值