使用 ThreadPoolExecutor 实现灵活的自定义线程池,并通过 ArrayBlockingQueue 存放任务。 针对每类不同的业务,分别定义不同的线程池,让它们互不影响。
使用线程池可以避免频繁地创建和销毁线程会带来显著的性能开销,线程池的管理通常依赖于特定的编程框架或库,其中
ThreadPoolExecutor
是Java中一个非常常用的线程池管理工具。
示例代码:
ExecutorService executorService =
new ThreadPoolExecutor(40, 1000, 10000, TimeUnit.MINUTES, new ArrayBlockingQueue<>(10000));
自定义线程池参数如下:
核心线程数(corePoolSize):线程池中一直保持活动的线程数。可以使用corePoolSize方法来设置。一般情况下,可以根据系统的资源情况和任务的特性来设置合适的值。
最大线程数(maximumPoolSize):线程池中允许存在的最大线程数。可以使用maximumPoolSize方法来设置。如果所有线程都处于活动状态,而此时又有新的任务提交,线程池会创建新的线程,直到达到最大线程数。
空闲线程存活时间(keepAliveTime):当线程池中的线程数量超过核心线程数时,如果这些线程在一定时间内没有执行任务,则这些线程会被销毁。可以使用keepAliveTime和TimeUnit方法来设置。
阻塞队列(workQueue):用于存放等待执行的任务的阻塞队列。可以根据任务的特性选择不同类型的队列,如LinkedBlockingQueue、ArrayBlockingQueue等。默认情况下,使用无界阻塞队列,即LinkedBlockingQueue,但也可以根据需要设置有界队列。
ArrayBlockingQueue
是一个由数组支持的有界阻塞队列。这个队列按 FIFO(先进先出)原则对元素进行排序。ArrayBlockingQueue
需要在创建时指定容量,且一旦创建后不能更改。此队列使用单个锁来控制插入和移除操作,从而导致这两种操作不能完全并行。
LinkedBlockingQueue
是一个由链表结构支持的可选有界队列。
LinkedBlockingQueue
内部使用两个锁,一个用于入队操作,一个用于出队操作,允许这两个操作并行进行,从而提高了队列在并发环境中的吞吐量。
PriorityBlockingQueue
是一个支持优先级排序的无界阻塞队列。队列中元素的排序可以根据自然排序,或者根据构造时提供的 Comparator
来进行。
PriorityBlockingQueue
通常用于执行基于优先级的任务调度。注意此队列不阻塞数据插入操作,但如果队列为空,数据取出操作会阻塞。
SynchronousQueue
是一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,反之亦然。
SynchronousQueue
适合于传递性的任务调度,每个任务都由一个线程提交,另一个线程接收执行。
import java.util.concurrent.*;
public class ThreadPoolWithArrayBlockingQueue {
public static void main(String[] args) {
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(10);
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 4, 1, TimeUnit.MINUTES, queue);
for (int i = 0; i < 20; i++) {
final int taskId = i;
executor.execute(() -> {
System.out.println("Executing task " + taskId +
" on thread " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
executor.shutdown();
}
}
在这个例子中,我们创建了一个有界队列 ArrayBlockingQueue
,用作线程池的工作队列。线程池被配置为有 2 个核心线程,最多 4 个线程,并且有一个容量为 10 的队列用于存储待处理任务。
线程工厂(threadFactory):用于创建线程的工厂。可以通过实现ThreadFactory接口自定义线程的创建逻辑。
拒绝策略(rejectedExecutionHandler):当线程池无法接受新的任务时,会根据设置的拒绝策略进行处理。常见的拒绝策略有AbortPolicy、DiscardPolicy、DiscardOldestPolicy和CallerRunsPolicy。
我们是会根据任务的类型以及消耗资源的情况来调整线程池的参数。
有一个经验值公式,其中 N 为 CPU 核心数:CPU 密集型任务,核心线程数设置为 N(或 N + 1);IO 密集型任务,核心线程数设置为 2N。