如何手写一个线程池(附线程池的用法)

本文详细介绍了如何设计一个线程池,包括考虑的核心线程数、最大线程数、任务队列和拒绝策略。通过分析线程池的工作流程,展示了如何创建工作线程、任务队列以及实现不同拒绝策略。同时,对比分析了JDK线程池的设计,探讨了其状态管理和工厂方法,并给出使用示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如何手写一个线程池(附线程池的用法)

设计一个线程池

首先,我们要设计线程池的目的是为了管理线程,所以它的核心就是提供线程来处理我们提交的任务。在设计之前我们先来思考一下几个问题:

  1、初始化的时候就创建线程还是提交任务的时候创建线程?
  2、假设我们提交的任务数大于了最大线程数怎么处理?直接抛弃?还是存放到队列里?
  3、这个队列有没有上限?如果存放到队列里队列达到了上限怎么办?
     1)直接抛弃?
     2)抛异常不让客户端提交?
     3)阻塞,等队列有空位再存这个任务?
     4)阻塞一段时间,时间过了队列还没有空位存就放弃提交任务?

1、我们当然希望有任务才去创建线程,因为如果这个线程池没有提交任务就创建了线程,那么这些线程就会空转或者睡眠,造成浪费。

2、当客户端提交的任务大于线程池的最大线程数maximumPoolSize时,我们也不应该把这个任务直接丢弃,因为线程池中的线程可能很快就把当前任务执行完。所以比较好的方式就是用个队列存储起来,当线程执行完了可以立马从线程池里面拿到任务执行。其实为了设计得更加合理一点应该设置一个核心线程数corePoolSize。

   1)当工作线程数wc < 核心线程数corePoolSize时,直接创建新的工作线程
   2)当工作线程数wc > = 核心线程数corePoolSize时,把任务存放到队列中。如果此时工作线程为0,我们应该开启空闲线程去处理我们提交的任务(核心线程数为0),不然我们的任务就只是存在队列中而没有被执行。所以我们还需要看看工作线程是否为0,如果为0则应该创建线程去处理这些任务。
   3)当队列满了的时候 & 当工作线程数wc <= 最大线程数maximumPoolSize,创建新的工作线程线程(空闲线程)
   4)当队列满了的时候 & 当工作线程数wc > 最大线程数maximumPoolSize,执行拒绝策略

3、一般来说我们的线程池的任务队列应该也要有个最大值,不能一直存,不然会有内存溢出的风险。但也不能排除有些场景需要用到无界队列,所以我们的队列也作一个简单的抽象,分为有界和无界队列。这里我们就只实现一个有界队列,有界队列实现了再看无界队列就很简单了。当这个队列满了的话,到底要不要放弃这个任务或者以什么方式放弃这个任务都应该视应用场景来划分,所以不同的场景会有不同的策略,我们这里可以使用策略模式对不同的场景设计不同的策略。
通过分析以上的三个问题,我们可以得到一个线程池的大概工作流程图:
在这里插入图片描述
接下来分析一下我们线程池的一个组成部分:workers(工作线程集合)、队列、拒绝策略以及一个线程工厂,还有核心线程数和最大线程数。除此之外,我们应该还要设计一个线程睡眠等待的最大时间,如果超过了这个时间还没有任务就把线程回收掉。然后我们的这个线程池还要有个核心的api就是submit来提交我们的任务。现在我们来设计一下我们这个线程池的类图(图可能画得不太好看,将就着看吧):

«interface» Executor execute(Runnable task) «interface» ThreadPool submit(Runnable task) processWorkerExit(Worker worker) AbstractThreadPool ThreadPoolExecutor Queue queue RejectedHandler handler AtomicInteger workCount ReentrantLock mainLock int corePoolSize int maximumPoolSize ThreadFactory factory HashSet<Worker> workers addworker(Runnable task, boolean core) «interface» RejectedHandler AbortPolicy CallerRunsPolicy «interface» Queue offer(Runnable run) poll() int size BlockingQueue ReentrantLock lock LinkedList<Runnable> queue int size long timeOut Condition empty Condition full «interface» Runnable Worker >>service>> iRunnable task Thread thread Queue queue ThreadPool pool public void Runnable getTask extend impl extend impl impl impl

设计代码

这里我也模仿一下jdk的源码设计两个接口:Executor和ThreadPool(ThreadPool继承Executor),Executor提供一个execute的api执行任务,ThreadPool则提供submit和processWorkerExit两个api用来提交任务和释放线程,这两个接口的代码如下:

public interface Executor {
   
   
    /**
     * 执行具体的task
     */
    void excute(Runnable task);
}

public interface ThreadPool extends Executor {
   
   
    /**
     * 处理提交的任务
     *
     * @param task
     */
    void submit(Runnable task);

    
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值