Java 线程池
前言:
创建过多的线程会占用更多的内存、并且在线程切换的时候增加消耗的资源和浪费更多的时间,为了缓解以上问题,出现一种基于复用和预分配思想的技术,线程池。线程池中的线程被统一创建和管理,提高了系统响应时间和系统的资源利用率。
除了线程池解决以上问题外,在java21中引入了虚拟线程。在Go中,引入了协程来缓解创建过多线程导致性能的问题,但他们不是基于复用和预分配的思想(池化技术),而是自己实现了一套高效的任务调度机制。笔者在此主要介绍Java的线程池技术。
主要内容:
- java的默认现程池
- 自定义线程池的参数配置
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
你知道线程池的 创建方式、7大参数、处理流程 和 最大线程数量该如何配置吗
标签:Java,队列,创建,任务,线程,new From: https://www.cnblogs.com/aoCat/p/17973401