一、简介
Java并发包中提供了线程池相关功能和类,使用线程池主要有以下几方面的作用
- 性能,当需要使用大量线程执行任务时,使用线程池可以提供较好的性能;如果不使用线程池,每个任务创建一个线程,而线程的创建和销毁会带来性能开销;使用线程池可以减少线程创建销毁带来的开销,因为线程池里面的线程是可以复用的,不用每次执行任务都重新创建和销毁线程。
- 资源限制和管理,线程池提供了限制线程资源、管理线程的手段,例如限制线程数、动态新增线程、线程执行任务数监控等。
- 可扩展性,线程池提供了多个可配置参数和可扩展接口,以满足不同的使用场景。
二、线程池对象
ThreadPoolExecutor.java
表示一个线程池,ThreadPoolExecutor
继承自AbstractExecutorService
,而AbstractExecutorService
实现了ExecutorService
接口
ExecutorService.java
public interface ExecutorService extends Executor, AutoCloseable {
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
}
ExecutorService
核心方法submit
用来提交任务给线程池执行,并且提供了不同的重载满足不同的场景
可以看到ExecutorService
继承了Executor
Executor.java
public interface Executor {
/**
* Executes the given command at some time in the future. The command
* may execute in a new thread, in a pooled thread, or in the calling
* thread, at the discretion of the {@code Executor} implementation.
*
* @param command the runnable task
* @throws RejectedExecutionException if this task cannot be
* accepted for execution
* @throws NullPointerException if command is null
*/
void execute(Runnable command);
}
Executor
的execute
方法用来执行异步任务。
三、线程池参数
ThreadPoolExecutor.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;
String name = Objects.toIdentityString(this);
this.container = SharedThreadContainer.create(name);
}
corePoolSize
:核心线程数,也就是线程池可以持有的线程个数,核心线程默认情况下就算空闲也不会被销毁,可以通过设置allowCoreThreadTimeOut
为true
允许空闲的核心线程销毁。默认情况下,线程池创建时并不会立即创建corePoolSize
,可以调用prestartCoreThread()
方法或者prestartAllCoreThreads()
提前创建好线程
maximumPoolSize
:线程池能够持有的最大线程个数,核心线程都被使用后如果还有任务提交给线程池,则线程池还可以创建最多maximumPoolSize
减corePoolSize
个线程来执行任务,给线程池提交任务时,如果所有核心线程都活动中,则任务被添加进阻塞队列,如果队列满了,则判断线程数是否小于maximumPoolSize
,是则创建新线程执行该任务
keepAliveTime
:超出核心线程的那部分线程存活时间,当线程池持有的线程数大于核心线程数时,超出核心线程的这部分线程在等待执行任务的最大时间,从线程空闲开始等待keepAliveTime
时间还没有任务提交给该线程执行,则该线程将会被销毁。如果keepAliveTime
为0,则超出corePoolSize
的线程空闲时将会立即被销毁
unit
:keepAliveTime
参数的时间单位,TimeUnit
对象
workQueue
:工作队列,用来存储提交给线程池被执行之前的任务,这个队列只存储被execute
方法提交的Runable
类型的任务
threadFactory
:线程工厂,当线程池需要创建线程时(核心线程都被占用),调用threadFactory
的newThread(Runnable r)
方法创建线程
handler
:任务执行拒绝策略,当线程池的被使用的线程已经达到maximumPoolSize
,并且workQueue
队列已满的情况下,提交新的任务给线程池执行时使用的处理策略
参数出现以下几种情况构造方法将抛出IllegalArgumentException
异常:
corePoolSize < 0
keepAliveTime < 0
maximumPoolSize <= 0
maximumPoolSize < corePoolSize
参数出现以下情况调用构造方法将抛出NullPointerException
异常:
workQueue==null
threadFactory==null
handler==null
设置allowCoreThreadTimeOut
参数:
默认情况下线程池的核心线程池是不会被销毁的,如果希望空闲的核心线程也被销毁则可以通过设置allowCoreThreadTimeOut
参数为true
实现。
ThreadPoolExecutor.java
public void allowCoreThreadTimeOut(boolean value) {
if (value && keepAliveTime <= 0)
throw new IllegalArgumentException("Core threads must have nonzero keep alive times");
if (value != allowCoreThreadTimeOut) {
allowCoreThreadTimeOut = value;
if (value)
interruptIdleWorkers(); // (1)
}
}
代码(1)设置allowCoreThreadTimeOut
为true
后,将会中断线程池中正在等待任务的线程。
四、线程池状态
RUNNING
:接收新任务并处理阻塞队列里的任务
SHUTDOWN
:拒绝新任务但处理阻塞队列里的任务
STOP
:拒绝新任务并抛弃阻塞队列里的任务,同时会中断正在处理的任务
TIDYING
:所有任务(包括阻塞队里里的任务)都执行完,后当前线程池的活动线程为0,将要调用terminated
方法
TERMINATED
:终止状态,terminated方法调用后的状态
线程池状态转换:
源状态 | 终状态 | 操作 |
---|---|---|
RUNNING | SHUTDOWN | 显式调用shutdown()方法,或者隐式调用finalize()方法里面的shutdown()方法 |
RUNNING/SHUTDOWN | STOP | 显示调用shotdownNow()方法 |
SHUTDOWN | TIDYING | 线程池和任务队列都为空 |
STOP | TIDYING | 当线程池为空 |
TIDYING | TERMINATED | 当terminated()方法执行完成时 |
五、创建线程池
ThreadPoolExecutor
类提供了多个不同参数的构成方法用来创建不同类型的线程池
ThreadPoolExecutor.java
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue);
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory);
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler);
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler);
除此之外,jdk提供了一个工具类Executors
,该类里面提供了很多静态方法用来创建不同类型的线程池,下面我们来看看Executors
类提供的创建线程池的静态方法
1、newFixedThreadPool(int nThreads)
该方法创建一个核心线程数和最大线程数都是nThreads
的线程池
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
可以看到设置keepAliveTime
为0表示如果有线程数大于核心线程数则空闲的线程会被销毁,阻塞队列为LinkedBlockingQueue
,其最大长度为Integer.MAX_VALUE
。
2、newSingleThreadExecutor()
该方法创建一个核心线程数和最大线程数都是1的线程池
public static ExecutorService newSingleThreadExecutor() {
return newSingleThreadExecutor(defaultThreadFactory());
}
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new AutoShutdownDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
可以看到设置keepAliveTime
为0表示如果有线程数大于核心线程数则空闲的线程会被销毁,阻塞队列为LinkedBlockingQueue
,其最大长度为Integer.MAX_VALUE
。
3、newCachedThreadPool()
该方法创建一个按需创建线程的线程池,核心线程数为0,最大可创建Integer.MAX_VALUE
个线程
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
设置keepAliveTime
为60表示如果有线程的空闲时间超过60秒则会被销毁,该线程池使用的是同步阻塞队列SynchronousQueue
,向SynchronousQueue
里面添加元素时,如果没有其他线程从SynchronousQueue
取元素则执行入队的线程会被阻塞,直到有其他线程执行出队操作。
4、newVirtualThreadPerTaskExecutor()
该方法对应jdk-21提供的虚拟线程,使用该方法创建的线程池会为每个任务创建一个虚拟线程,并且创建的线程数没有限制
public static ExecutorService newVirtualThreadPerTaskExecutor() {
ThreadFactory factory = Thread.ofVirtual().factory();
return newThreadPerTaskExecutor(factory);
}
5、newScheduledThreadPool(int corePoolSize)
该方法创建一个线程池,核心线程数为corePoolSize
,最大线程数为Integer.MAX_VALUE
,该线程池可以在指定延迟时间后执行任务或者定时执行任务
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
private static final long DEFAULT_KEEPALIVE_MILLIS = 10L;
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
可以看到可以看到设置keepAliveTime
为10表示如果有线程数大于核心线程数则空闲的线程在10秒后会被销毁,阻塞队列使用的是DelayedWorkQueue
延时队列
六、DefaultThreadFactory
默认情况下,我们创建线程池时如果不指定线程工厂,则都会使用DefaultThreadFactory
线程工厂创建线程,该类有一个变量threadNumber
用来记录每个工厂创建的线程数,除此之外还有一个static
修饰的类变量poolNumber
,用来记录创建工厂对象的数量,从代码(1)和代码(2)可以看到这两个变量将会应用到工厂创建出来的线程名称上。
DefaultThreadFactory.java
private static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
@SuppressWarnings("removal")
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-"; // (1)
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(), // (2)
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}