Java并发编程(6)-线程池总结

本文详细探讨了线程池的概念,介绍了线程池为何能够提升系统性能,深入分析了Java Executor框架及其核心组件ThreadPoolExecutor。文章还对比了四种常见的线程池类型,包括固定线程池、缓存线程池、单一线程池和定时线程池,并讨论了如何合理确定线程池的线程数量。

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

一.为什么要使用线程池

1. 降低资源消耗
2. 减少反复创建线程带来的开销
3. 提高获取线程的响应速度

二.Executor框架

1.什么时Executor框架

Excutor框架与JDK 1.5被引入,用于代替线程的手动start启动,并且为我们提供了线程池的支持等等。
Executor 框架不仅包括了线程池的管理,还提供了线程工厂、队列以及拒绝策略等,Executor 框架让并发编程变得更加简单。

2.Executor框架的组成有哪些

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-43XQIAKi-1574217742215)(http://note.youdao.com/yws/res/3285/BD9F714067A945A8B320D48DEA5F0CF4)]
主要可以分为三个部分:

  1. 任务(Runnable和Callable):任务是Executor提交执行的最小单位,只要实现了Runnable接口或Callable接口并重写相关方法即可被作为任务执行。
  2. 任务执行器(Executor):任务执行器的任务是提交任务(提交Callable任务还可获取到执行结果)。任务执行器需要实现ExecutorService接口,该接口继承自Executor抽象类。常见的任务执行器有ThreadPollExecutor和ScheduledThreadPollExecutor。
    在这里插入图片描述
  3. 异步计算结果(Future)
    Future 接口以及 Future 接口的实现类 FutureTask 类都可以代表异步计算的结果。
    当我们把 Runnable接口 或 Callable 接口 的实现类提交给 ThreadPoolExecutor 或 ScheduledThreadPoolExecutor 执行。(调用 submit() 方法时会返回一个 FutureTask 对象)

三、ThreadPoolExecutor相关

ThreadPoolExecutor位于java.util.concurrent并发包下,为一些Executor提供了基本的实现,在JDK1.5中由Executors中的工厂中的newCachedThreadPoolnewFixedThreadPoolnewScheduledThreadExecutor方法所返回。但在JDK1.7之后,不能通过Executors去获得ThreadPoolExecutor了,而是需要自己手动去定制。ThreadPoolExecutor是一个灵活的、健壮的池实现,允许用户进行各种各样的定制

1.ThreadPoolExecutor的构造方法参数
  * 用给定的初始参数创建一个新的ThreadPoolExecutor。
     */
    public ThreadPoolExecutor(int corePoolSize,//线程池的核心线程数量
                              int maximumPoolSize,//线程池的最大线程数
                              long keepAliveTime,//当线程数大于核心线程数时,多余的空闲线程存活的最长时间
                              TimeUnit unit,//时间单位
                              BlockingQueue<Runnable> workQueue,//任务队列,用来储存等待执行任务的队列
                              ThreadFactory threadFactory,//线程工厂,用来创建线程,一般默认即可
                              RejectedExecutionHandler handler//拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务
                               )
  1. corePoolSize :
    核心线程池大小,当线程池中的线程数目小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。
  2. maximumPoolSize:
    最大线程池大小,表示当前池中可以活跃的最大线程数目。
  3. keepAliveTime :
    线程池中超过corePoolSize数目的空闲线程最大存活时间。
  4. TimeUnit:
    keepAliveTime时间单位。可以设置为TimeUnit.MICROSECONDSTimeUnit.SECONDS等单位,分别表示毫秒和秒。
  5. workQueue :
    阻塞任务队列,用以存储超出了corePoolSize的线程
  6. threadFactory:
    新建线程工厂。如果没有的话则使用系统默认的。
  7. RejectedExecutionHandler:
    当提交任务数超过maxmumPoolSize与workQueue之和时,任务会交给RejectedExecutionHandler来处理。如果没有的话则使用系统默认的。
2.不断地往线程池中添加任务

在这里插入图片描述

在这里插入图片描述

  1. 当任务的数量增加时,会增加core线程来处理,但此时线程数量不会超过corePoolSize;
  2. 当core线程全部被占用着且已经达到了corePoolSize,此时不会立即创建新的线程,而是将任务扔到任务队列里面,并且用core线程去执行;
  3. 任务队列满了(意味着插入任务失败),则开始创建MAX线程去处理它们;若线程数达到MAX后,队列还一直是满的(无法处理),则抛出RejectedExecutionException。
3.ThreadPoolExecutor的饱和策略有哪些

首先明确一点,饱和策略会在ThreadPoolExecutor无法处理新增的任务时起作用,即线程数量已经达到了max,且当前的工作队列是满的,无法处理新的任务是:

1.AbortPolicy

抛出RejectedExecutionException异常,即无法处理异常。

2.CallerRunsPolicy

既不抛弃任务也不抛出异常,而是将某些任务回退到调用者,让调用者去执行它。

3.DiscardPolicy

将不能够处理的任务抛弃,不执行它。

4.DiscardOldestPolicy

将任务队列中最老的任务丢弃,而将新的任务放入任务队列中。

4.有哪些线程池
1.newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(
                                    nThreads,// corePoolSize
                                    nThreads, // maxmiPoolSize
                                  0L, TimeUnit.MILLISECONDS,
                                  //使用一个基于FIFO排序的阻塞队列,在所有corePoolSize线程都忙时新任务将在队列中等待
                                  new LinkedBlockingQueue<Runnable>());
}

创建可重用且固定线程数的线程池,如果线程池中的所有线程都处于活动状态,此时再提交任务就在队列中等待,直到有可用线程;如果线程池中的某个线程由于异常而结束时,线程池就会再补充一条新线程。原理是有界的阻塞队列。
在这里插入图片描述

2.newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, // corePoolSize = 0
                                  Integer.MAX_VALUE, // maximPoolSize=2^31 - 1
                                  60L, TimeUnit.SECONDS, // 空闲时间60s
                                 //使用同步队列,将任务直接提交给线程
                                  new SynchronousQueue<Runnable>());
}

创建可缓存的线程池,如果线程池中的线程在60秒未被使用就将被移除,在执行新的任务时,当线程池中有之前创建的可用线程就重用可用线程,否则就新建一条线程。

在这里插入图片描述

3.newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
   return new FinalizableDelegatedExecutorService
                     //corePoolSize和maximumPoolSize都等于,表示固定线程池大小为1
                        (new ThreadPoolExecutor(1, // corePoolSize=1 
                                                1,// maximPoolSize=1
                                                0L, TimeUnit.MILLISECONDS,
                                                new LinkedBlockingQueue<Runnable>()));
}

创建一个单线程的Executor,如果该线程因为异常而结束就新建一条线程来继续执行后续的任务。

在这里插入图片描述

4.newScheduledThreadPool

创建一个可延迟执行或定期执行的线程池。

5.如何确定线程池中需要多少个线程

一般来说,线程池中的线程数量不可盲目地确定和选择,因为如果线程池中线程数量太大,那么CPU会对许多线程进行上下文切换,从而导致效率的低下;如果线程数量太少,又会导致CPU核心数无法被充分地利用。

根据任务的不同,可将任务分为CPU密集型任务与IO密集型任务,它们的区别是CPU密集型任务需要充分地使用CPU操作,几乎不需要CPU等待;而IO密集型任务需要等待IO阻塞,所以CPU会有相对多的空闲时间。以下的N表示N核CPU:

1. CPU密集型任务:N + 1
2. IO密集型任务:2 * N

四、相关问题

1.Runnable与Callable

1. Runnable:无返回值的任务。且任务方法为run()

public interface Runnable {
   /**
    * 被线程执行,没有返回值也无法抛出异常
    */
    public abstract void run();
}

2. Callable:有返回值的任务。且任务方法为V call()

public interface Callable<V> {
    /**
     * 计算结果,或在无法这样做时抛出异常。
     * @return 计算得出的结果
     * @throws 如果无法计算结果,则抛出异常
     */
    V call() throws Exception;
}
2.execute()和submit()
  1. execute():提交的是Runnable,且不能接受返回值。
  2. submit():提交的是Callable,且能接受返回值FutureTask。
    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }
    protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new FutureTask<T>(runnable, value);
    }
3.shutdown()和shutdownNow()

shutdown()和shutdownNow()内部实现是依次调用线程的interrupt方法去终止线程执行,但不同之处在于shutdown()是终止已经执行完成任务的线程而不会去终止正在执行任务的进程,但shutdownNow()会终止所有状态的线程,无论其是不是正在执行任务。

4.isTerminated()和isShutdown()
  1. isTerminated():当线程池中全部线程都已经关闭时,返回 true。
  2. isShutdown():当线程池调用shutdown()或shutdownNow()方法时,返回true。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BoringRong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值