手撸一个线程池

常规开头

  • 多线程编程是在开发过程中非常基础且非常重要的一个环节,基本上任何一家软件公司或者项目中都会使用多线程。当然在项目中通常都是通过线程池的方式执行多线程任务。看线程池执行流程和源码设计有助于提升我们多线程编程技术和解决工作中遇到的问题。
  • 很久之前就看过ThreadPoolExecutor线程池源码,了解其执行过程。这次准备手撸一个简单版的线程池加强一下对执行流程的理解。

简单的过程

  • 废话不多说,直接上代码
/**
 * @Author: ZRH
 * @Date: 2021/3/5 15:13
 */
public class MyThreadPool {

    /**
     * 核心工作线程数
     */
    private volatile int coreThreadPoolSize;
    /**
     * 工作线程集合
     */
    private final HashSet<Runnable> workSet = new HashSet<>();
    /**
     * 线程池等待队列
     */
    private final BlockingQueue<Runnable> workQueue;

    /**
     * 初始化线程池构造方法
     *
     * @param coreThreadPoolSize 核心工作线程数
     * @param workQueue          线程等待队列
     */
    public MyThreadPool(int coreThreadPoolSize, BlockingQueue<Runnable> workQueue) {
        if (coreThreadPoolSize <= 0 || workQueue == null) {
            throw new RuntimeException();
        }
        this.coreThreadPoolSize = coreThreadPoolSize;
        this.workQueue = workQueue;
    }

    /**
     * 执行线程任务
     *
     * @param runnable
     */
    public void execute(Runnable runnable) {
        // 判断线程集合大小是否超过核心线程数
        if (workSet.size() < coreThreadPoolSize) {
            final Worker worker = new Worker(runnable);
            workSet.add(worker);
            worker.thread.start();
            System.out.println("任务加入工作线程");
            return;
        }
        // 判断任务进入队列是否成功
        if (workQueue.offer(runnable)) {
            System.out.println("任务加入队列");
            return;
        }
        // 走拒绝策略
        System.out.println("线程池队列满了,拒绝任务");
        ...
    }

    /**
     * 线程池内部工作线程
     */
    class Worker implements Runnable {

        /**
         * 需要执行的任务
         */
        private Runnable task;
        /**
         * 在外部不能直接运行worker.run(),否则会使线程池阻塞。
         * 所以有获取当前Worker线程对象,在执行线程任务时通过start()执行当前工作线程。
         */
        private final Thread thread;

        public Worker(Runnable runnable) {
            this.task = runnable;
            this.thread = new Thread(this);
        }

        public void run() {
            try {
                // 先获取Worker中的任务,再从workQueue中获取任务
                Runnable task = this.task;
                while (task != null || (task = workQueue.take()) != null) {
                    task.run();
                    task = null;
                }
            } catch (Exception e) {
            }
        }
    }
}
  • 测试一下执行效果
    public static void main(String[] args) {
        // 创建一个核心线程数为2,队列为5的线程池
        MyThreadPool myThreadPool = new MyThreadPool(2, new ArrayBlockingQueue<>(5));
        // 执行10个线程任务
        for (int i = 0; i < 10; i++) {
            myThreadPool.execute(() -> {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                }
            });
        }
    }
----------------------------
任务加入工作线程
任务加入工作线程
任务加入队列
任务加入队列
任务加入队列
任务加入队列
任务加入队列
线程池队列满了,拒绝任务
线程池队列满了,拒绝任务
线程池队列满了,拒绝任务
  • 这里一共有执行了10个任务,其中2两个加入工作线程,5个加入队列,剩下的都走拒绝流程。

结尾了

  • 上面的示例代码是一个很基础的线程池实现,是基于ThreadPoolExecutor线程池执行流程实现。
  • 当然其中还缺少了很多关键的点:比如线程池的状态,线程池的锁,线程任务的拒绝策略等等。正是因为少了这些关键的步骤,导致上面代码也是比较简单理解。了解其整个执行流程会对我们看ThreadPoolExecutor线程池源码会更容易理解。-_-
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值