ThreadPoolExecutor线程池的深入理解

最近实验室很多同学也在开始内推和网申,也有很多同学已经开始面试了,至于线程池嘛,一直以来都是面试的重灾区,明明知道会被问道,但好多同学依然不能给予面试官满意的答案,所以在此我跟大家分享一下至关重要的线程池相关问题吧,希望大家都能够深入的理解掌握线程池这一重要的知识点吧~

一、首先我们要知道究竟线程池是何方神物呢?

         线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。

二、线程池是怎样实现的呢?

     从大的方面来讲,线程池是通过线程池框架Executor来实现的,Executor框架包括了类Executor,Executors,ExecutorService,ThreadPoolExecutor ,Callable,Future和FutureTask的使用等。Executor是所有线程池的最高级接口,它里面只定义一个方法execute(),这也是所有线程池实现必须实现的核心方法,ExecutorService:接口增加了Executor的行为,是Executor实现类的最直接接口,而Executors则作为线程池的一个工具类提供了一系列工厂方法用于创建线程池,返回的线程池都实现了ExecutorService 接口。那么重点来了,线程池具体是由哪个类实现的呢,ThreadPoolExecutor类便会浮现在我们脑海里了,这无疑是线程池最最核心的类了,所有我们常用的线程池都是基于这个类实现的,它有多种构造方法,这里我给大家演示一种常见的五参数构造方法:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {

        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);

}

那么在线程池框架中,ThreadPoolExecutor实现类处于什么样的位置呢,下面我给大家放一张我自己画的图,额额~

画的真心丑尴尬,不过大家能看清楚依赖和继承关系就好了,其实对于AbstractExecutorService抽象实现类,它已经近乎实现了ExecutorService接口的所有方法了,而ThreadPoolExecutor则更加完善了,继承了父类的所有方法并且完成了所有父类未实现的方法。而定时线程池池类则又继承了ThreadPoolExecutor类,在继承该类方法基础上又增加了定时线程功能......

三、ThreadPoolExecutor类的解析

我们都知道了ThreadPoolExecutor是线程池的核心类,那么我们就来深入了解一下它吧,ThreadPoolExecutor类包含哪些元素呢,这里我们先来了解一下它有哪些属性:

1)corePoolSize:线程池的核心线程数,线程池中运行的线程数也永远不会超过 corePoolSize 个,默认情况下可以一直存活。可以通过设置allowCoreThreadTimeOut为True,此时核心线程数就是0,此时keepAliveTime控制所有线程的超时时间2)maximumPoolSize:线程池允许的最大线程数;

3)keepAliveTime: 指的是空闲线程结束的超时时间;

4)unit :是一个枚举,表示 keepAliveTime 的单位;

5)workQueue:表示存放任务的BlockingQueue<Runnable队列。

6)BlockingQueue:阻塞队列(BlockingQueue)是java.util.concurrent下的主要用来控制线程同步的工具。如果BlockQueue是空的,从BlockingQueue取东西的操作将会被阻断进入等待状态,直到BlockingQueue进了东西才会被唤醒。同样,如果BlockingQueue是满的,任何试图往里存东西的操作也会被阻断进入等待状态,直到BlockingQueue里有空间才会被唤醒继续操作。

了解了这些属性,我们也介绍了ThreadPoolExecutor的构造方法,那么我们就来看一下怎么通过它创建线程池的呢~

生成线程池采用了工具类Executors的静态方法,以下是几种常见的线程池:

1)SingleThreadExecutor:单个后台线程 (其缓冲队列是无界的)

public static ExecutorService newSingleThreadExecutor() {        
    return new FinalizableDelegatedExecutorService (
        new ThreadPoolExecutor(1, 1,                                    
        0L, TimeUnit.MILLISECONDS,                                    
        new LinkedBlockingQueue<Runnable>()));   
}

创建一个单线程的线程池。这个线程池只有一个核心线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

2)FixedThreadPool:只有核心线程的线程池,大小固定 (其缓冲队列是无界的) 。

public static ExecutorService newFixedThreadPool(int nThreads) {         
        return new ThreadPoolExecutor(nThreads, nThreads,                                       
            0L, TimeUnit.MILLISECONDS,                                         
            new LinkedBlockingQueue<Runnable>());     
}

创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

3)CachedThreadPool:无界线程池,可以进行自动线程回收。

public static ExecutorService newCachedThreadPool() {         
    return new ThreadPoolExecutor(0,Integer.MAX_VALUE,                                           
           60L, TimeUnit.SECONDS,                                       
           new SynchronousQueue<Runnable>());     
}

如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。SynchronousQueue是一个是缓冲区为1的阻塞队列。

4)ScheduledThreadPool:核心线程池固定,大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

public static ExecutorService newScheduledThreadPool(int corePoolSize) {         
    return new ScheduledThreadPool(corePoolSize, 
              Integer.MAX_VALUE,                                                  
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,                                                    
              new DelayedWorkQueue());    
}

四、线程池的工作过程

创建好了线程池后,我们自然应该了解一下它的工作过程了~

  1. 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。
  2. 当调用 execute() 方法添加一个任务时,线程池会做如下判断:如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列;如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常RejectExecutionException。
  3. 当一个线程完成任务时,它会从队列中取下一个任务来执行。
  4. 当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。

五、线程池提交任务的常用方法

       在线程池的工作过程中,我们知道了线程池是需要添加提交任务的,那么它是怎样提交任务,用哪些方法提交的呢~      

线程池最常用的提交任务的方法有两种:

1)execute:

ExecutorService.execute(Runnable runable);

2)submit:

FutureTask task = ExecutorService.submit(Runnable runnable);

FutureTask<T> task = ExecutorService.submit(Runnable runnable,T Result);

FutureTask<T> task = ExecutorService.submit(Callable<T> callable);

submit(Callable callable)的实现,submit(Runnable runnable)同理~

public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    FutureTask<T> ftask = newTaskFor(task);
    execute(ftask);
    return ftask;
}

可以看出submit开启的是有返回结果的任务,会返回一个FutureTask对象,这样就能通过get()方法得到结果。submit最终调用的也是execute(Runnable runable),submit只是将Callable对象或Runnable封装成一个FutureTask对象,因为FutureTask是个Runnable,所以可以在execute中执行。

六、线程池的作用

说了这么多,创建线程池究竟有什么好处呢~

1)重用线程池中的线程,减少因对象创建,销毁所带来的性能开销;

2)能有效的控制线程的最大并发数,提高系统资源利用率,同时避免过多的资源竞争,避免堵塞;

3)使用线程池可以对线程进行简单管理,使线程的使用更加简单高效。

       至此我大概把线程池的基础内容给大家介绍完了,建议大家有什么不懂的可以看一下源码,因为线程池这一块知识确实是比较重要的,所以希望大家都能理解掌握哦~

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ThreadPoolExecutor是Java中的一个线程池实现类。它继承自ExecutorService接口,可以用来管理和执行线程任务。ThreadPoolExecutor线程池提供了更灵活的线程管理和任务调度的功能,并且可以根据需要进行配置。可以通过指定核心线程数、最大线程数、线程存活时间和任务队列等参数来创建和配置ThreadPoolExecutor线程池。 使用ThreadPoolExecutor线程池可以提供以下几个优点: 1. 降低线程创建和销毁的开销。线程池可以重用已经创建的线程,减少了频繁创建和销毁线程的开销。 2. 提高系统的响应速度。线程池可以并发执行多个任务,提高了系统的处理能力和响应速度。 3. 控制线程并发数量。通过设置线程池的核心线程数和最大线程数,可以控制系统的并发线程数量,避免资源耗尽和系统崩溃的风险。 4. 提供任务调度和管理。线程池可以将任务按照一定的策略和优先级进行调度和执行,方便管理任务的执行顺序和优先级。 总之,ThreadPoolExecutor线程池是一个灵活可配置的线程管理和任务调度工具,可以提高系统的并发处理能力和响应速度。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [线程池ThreadPoolExecutor详解(整理详细)](https://blog.csdn.net/trusause/article/details/125747447)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [ThreadPoolExecutor线程池的使用方法](https://download.csdn.net/download/weixin_38659648/12746355)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值