Java线程池ThreadPoolExecutor

一、简介

Java并发包中提供了线程池相关功能和类,使用线程池主要有以下几方面的作用

  1. 性能,当需要使用大量线程执行任务时,使用线程池可以提供较好的性能;如果不使用线程池,每个任务创建一个线程,而线程的创建和销毁会带来性能开销;使用线程池可以减少线程创建销毁带来的开销,因为线程池里面的线程是可以复用的,不用每次执行任务都重新创建和销毁线程。
  2. 资源限制和管理,线程池提供了限制线程资源、管理线程的手段,例如限制线程数、动态新增线程、线程执行任务数监控等。
  3. 可扩展性,线程池提供了多个可配置参数和可扩展接口,以满足不同的使用场景。

二、线程池对象

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);
}

Executorexecute方法用来执行异步任务。

三、线程池参数

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:核心线程数,也就是线程池可以持有的线程个数,核心线程默认情况下就算空闲也不会被销毁,可以通过设置allowCoreThreadTimeOuttrue允许空闲的核心线程销毁。默认情况下,线程池创建时并不会立即创建corePoolSize,可以调用prestartCoreThread()方法或者prestartAllCoreThreads()提前创建好线程

maximumPoolSize:线程池能够持有的最大线程个数,核心线程都被使用后如果还有任务提交给线程池,则线程池还可以创建最多maximumPoolSizecorePoolSize个线程来执行任务,给线程池提交任务时,如果所有核心线程都活动中,则任务被添加进阻塞队列,如果队列满了,则判断线程数是否小于maximumPoolSize,是则创建新线程执行该任务

keepAliveTime:超出核心线程的那部分线程存活时间,当线程池持有的线程数大于核心线程数时,超出核心线程的这部分线程在等待执行任务的最大时间,从线程空闲开始等待keepAliveTime时间还没有任务提交给该线程执行,则该线程将会被销毁。如果keepAliveTime为0,则超出corePoolSize的线程空闲时将会立即被销毁

unitkeepAliveTime参数的时间单位,TimeUnit对象

workQueue:工作队列,用来存储提交给线程池被执行之前的任务,这个队列只存储被execute方法提交的Runable类型的任务

threadFactory:线程工厂,当线程池需要创建线程时(核心线程都被占用),调用threadFactorynewThread(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)设置allowCoreThreadTimeOuttrue后,将会中断线程池中正在等待任务的线程。

四、线程池状态

RUNNING:接收新任务并处理阻塞队列里的任务

SHUTDOWN:拒绝新任务但处理阻塞队列里的任务

STOP:拒绝新任务并抛弃阻塞队列里的任务,同时会中断正在处理的任务

TIDYING:所有任务(包括阻塞队里里的任务)都执行完,后当前线程池的活动线程为0,将要调用terminated方法

TERMINATED:终止状态,terminated方法调用后的状态

线程池状态转换:

源状态终状态操作
RUNNINGSHUTDOWN显式调用shutdown()方法,或者隐式调用finalize()方法里面的shutdown()方法
RUNNING/SHUTDOWNSTOP显示调用shotdownNow()方法
SHUTDOWNTIDYING线程池和任务队列都为空
STOPTIDYING当线程池为空
TIDYINGTERMINATED当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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值