Java 线程池

Java 线程池

前言:
创建过多的线程会占用更多的内存、并且在线程切换的时候增加消耗的资源和浪费更多的时间,为了缓解以上问题,出现一种基于复用和预分配思想的技术,线程池。线程池中的线程被统一创建和管理,提高了系统响应时间和系统的资源利用率。
除了线程池解决以上问题外,在java21中引入了虚拟线程。在Go中,引入了协程来缓解创建过多线程导致性能的问题,但他们不是基于复用和预分配的思想(池化技术),而是自己实现了一套高效的任务调度机制。笔者在此主要介绍Java的线程池技术。
主要内容:

  1. java的默认现程池
  2. 自定义线程池的参数配置

JAVA的线程池配置

java创建一个线程池是通过以下代码:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

可以从传入的参数了解到创建一个线程池需要的参数有如下表:

参数 解释 作用 是否必需
corePoolSize 核心线程的数量 执行工作的线程,当任务到来,但线程数量小于核心现程数量,便会创建新的线程。线程数量大于核心线程,队列没满,把任务放入任务队列 必须
maximumPoolSize 最大线程数量 当任务到来,并且任务队列满且线程数少于最大线程数,便增加线程数量。 必需
keepAliveTime 空闲时间 当线程数量大于核心线程数量时,会销毁空闲了该时间的现程 必需
TimeUnit unit 时间单位 用于时间单位 必需
workQueue 任务队列 存放需要线程处理的任务 必需
threadFactory 线程工厂 创建基本线程的方式 非必需
RejectedExecutionHandler handler 拒接策略 当线程大于等于最大线程数且任务队列满,则执行拒绝策略 非必需

任务拒绝策略有以下选择:

拒绝策略 作用 使用场景
CallerRunsPolicy 当触发拒绝策略,只要线程池没有关闭的话,则使用调用线程直接运行任务。 对并发要求不高的场景,调用线程执行会影响当前线程执行的任务
AbortPolicy 丢弃任务,并抛出拒绝执行 RejectedExecutionException 异常信息 需要自己处理拒绝策略的情况下使用,比如Tomcat的线程池
DiscardPolicy 直接丢弃,其他啥都没有 任务不重要的情况下
DiscardOldestPolicy 当触发拒绝策略,只要线程池没有关闭的话,丢弃阻塞队列 workQueue 中最老的一个任务,并将新任务加入 任务一般重要,要求尽力处理的情况下,并且新任务比老任务重要

JAVA的默认线程池

FixedThreadPool

FixedThreadPool是固定长度线程池,由创建时自己设置线程池的线程数量。

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

从参数可以知道,这个Fixed线程池是个固定大小的线程池,并且任务队列是无界队列,没有拒绝策略,来多少任务处理多少任务。如果任务增加的速度大于现场池处理的速度,就容易造成内存泄露。

CachedThreadPool

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

从初始参数可以看出,没有核心线程大小,有最大线程大小,且最大线程为Inter的最大值,SynchronousQueue为任务队列。这个队列没有存储空间。意味着来多少任务,处理多少任务。这个线程池容易因创建大量线程而引起系统瘫痪,不适合处理花费时间长的任务,适合处理,花费时间小的任务。

SingleThreadExecutor

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

这个是单线程线程池,用于执行有先后顺序关联的任务。同样任务队列是无界的,需要注意内存溢出。

ScheduledThreadPool

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

该线程池主要用于执行定时任务或者延时任务。在java中还有个Timer类专门用于处理延时任务或者定时任务,但Timer中的任务会彼此影响执行的时间,导致时间的不准确。如果有多个延时任务或者定时任务可以使用ScheduledThreadPool 线程池来执行任务。并且一个任务出现异常也不会影响其它的任务执行。

引用:
Java中的线程池(4)----ScheduledThreadPool

Java默认提供的线程池

你知道线程池的 创建方式、7大参数、处理流程 和 最大线程数量该如何配置吗

posted @   黑猫魔法师  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示
点击右上角即可分享
微信分享提示