我们借用JDK自带的线程池ThreadPoolExecutor讲解线程池思想,打算分三个主题来讲:
- corePoolSize、maximumPoolSize与workQueue
- ThreadFactory
- RejectedExecutionHandler
本文先讲第一个主题。
1、corePoolSize、maximumPoolSize与workQueue
线程池的运行过程:
为了方便测试的时候,了解线程的调用情况,因此改写了ThreadFactory,线程名称前缀为THREAD-POOL-,按序号递增,这样我们就能清楚看到线程调用的是核心线程还是拓展线程。由于JDK线程池是先创建核心线程,后面有需要再创建拓展线程,因此index <= corePoolSize的都是为核心线程,其他的则为拓展线程。
public class AsyncThreadFactory implements ThreadFactory {
private final AtomicInteger index = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, String.format("THREAD-POOL-%s", index.getAndIncrement()));
}
}
测试代码:
public class AsyncUtilMain {
/**
* 核心线程数
*/
private static final int CORE_POOL_SIZE = 10;
/**
* 最大线程数
*/
private static final int MAX_POOL_SIZE = 100;
/**
* 阻塞队列长度
*/
private static final int QUEUE_SIZE = 10;
/**
* 需求线程数
*/
private static final int REQUIREMENT_POOL_SIZE = 10;
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE, 60, TimeUnit.MINUTES, new LinkedBlockingQueue<>(QUEUE_SIZE),
new AsyncThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
for (int index = 0; index < REQUIREMENT_POOL_SIZE; index++) {
int value = index;
threadPoolExecutor.submit(() -> System.out.println(Thread.currentThread().getName() + ":" + value));
}
threadPoolExecutor.shutdown();
}
}
根据上述流程图,线程池的运行情况会有以下四种:
1、corePoolSize >= 需求线程数
10个核心线程数, 90个可拓展线程数, 阻塞队列数10个
需求线程数为10个, 因此所有的线程均可被核心线程数调用
所以运行结果应该为1~10号线程(核心线程)完成整个过程
运行结果:
THREAD-POOL-2:1
THREAD-POOL-4:3
THREAD-POOL-3:2
THREAD-POOL-1:0
THREAD-POOL-5:4
THREAD-POOL-6:5
THREAD-POOL-7:6
THREAD-POOL-8:7
THREAD-POOL-9:8
THREAD-POOL-10:9
2、corePoolSize + workQueueSize >= 需求线程数
5个核心线程数, 5个可拓展线程数, 阻塞队列长度为5
需求线程数为10个, 因此5个核心线程会马上消费5个需求线程,剩下的5个会在阻塞队列中等待5个核心线程消费。
所以运行结果应该为1~5号线程(核心线程)完成整个过程
配置代码:
/**
* 核心线程数
*/
private static final int CORE_POOL_SIZE = 5;
/**
* 最大线程数
*/
private static final int MAX_POOL_SIZE = 10;
/**
* 阻塞队列长度
*/
private static final int QUEUE_SIZE = 5;
/**
* 需求线程数
*/
private static final int REQUIREMENT_POOL_SIZE = 10;
运行结果:
THREAD-POOL-3:2
THREAD-POOL-4:3
THREAD-POOL-1:0
THREAD-POOL-2:1
THREAD-POOL-1:7
THREAD-POOL-4:6
THREAD-POOL-5:4
THREAD-POOL-3:5
THREAD-POOL-1:9
THREAD-POOL-2:8
3、corePoolSize + workQueueSize < 需求线程数
5个核心线程数, 5个可拓展线程数, 阻塞队列长度为3
需求线程数为10个, 因此5个核心线程会马上消费5个需求线程,剩下的3个会在阻塞由于阻塞队列溢出,那么剩下的两个需求线程将会触发可拓展线程,因此实际运行的线程应该为1~5号线程(核心线程)和 6 ~ 7号线程(可拓展线程)
配置代码:
/**
* 核心线程数
*/
private static final int CORE_POOL_SIZE = 5;
/**
* 最大线程数
*/
private static final int MAX_POOL_SIZE = 10;
/**
* 阻塞队列长度
*/
private static final int QUEUE_SIZE = 3;
/**
* 需求线程数
*/
private static final int REQUIREMENT_POOL_SIZE = 10;
运行结果:
THREAD-POOL-1:0
THREAD-POOL-6:8
THREAD-POOL-1:5
THREAD-POOL-6:6
THREAD-POOL-5:4
THREAD-POOL-2:1
THREAD-POOL-4:3
THREAD-POOL-7:9
THREAD-POOL-3:2
THREAD-POOL-1:7
4、maximumPoolSize+ workQueueSize + < 需求线程数
这种情况是最大线程数和阻塞队列都不足以满足需求线程数,那么这时就会触发线程池配置的拒绝策略:
配置代码:
/**
* 核心线程数
*/
private static final int CORE_POOL_SIZE = 5;
/**
* 最大线程数
*/
private static final int MAX_POOL_SIZE = 10;
/**
* 阻塞队列长度
*/
private static final int QUEUE_SIZE = 3;
/**
* 需求线程数
*/
private static final int REQUIREMENT_POOL_SIZE = 15;
运行结果:
THREAD-POOL-10:12
THREAD-POOL-5:4
THREAD-POOL-4:3
Exception in thread “main” THREAD-POOL-5:5
THREAD-POOL-6:8
THREAD-POOL-7:9
THREAD-POOL-9:11
THREAD-POOL-8:10
THREAD-POOL-2:1
THREAD-POOL-10:7
THREAD-POOL-4:6
THREAD-POOL-1:0
THREAD-POOL-3:2
java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@4e50df2e rejected from java.util.concurrent.ThreadPoolExecutor@1d81eb93[Running, pool size = 10, active threads = 8, queued tasks = 1, completed tasks = 2]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
at cn.gaaidou.web.server.socket.AsyncUtilMain.main(AsyncUtilMain.java:40)
由于代码配置的拒绝策略是ThreadPoolExecutor.AbortPolicy,因此线程池直接抛出异常。