前言:
最近分析Java源码,对Java的线程池有一些疑惑,特提出来,希望大家能一起讨论确认一下。
If there are more than corePoolSize but less than maximumPoolSize threads running, a new thread will be created only if the queue is full
3.个人觉得这个”is full” 的条件真的是 很傻的设计, 比较合理的设计应该是 “线程数根据提交的任务,自动在 corePoolSize 和 maximumPoolSize 中调整, 和队列是否满没有关系。这样才能够在任务压力低时降低负荷,在任务压力高时及时处理”。
考虑如下场景和需求:
一个服务器希望通过线程池进行服务,为了自动适应网络请求,希望根据请求的任务数自动调整线程数:
- 线程数:最低值为 CPU个数, 最高值为 CPU个数 x 2
- 队列任务数:为了不丢失请求,不设置队列上限
相信这种场景,应该是一个比较常见的需求,但 ThreadPoolExecutor 却无法满足?
当然,一般队列都会设置上限,避免出现内存等问题,不过设置的大的话; 线程无法自动增加,设置的小的话,又会遇到 RejectedExecutionHandler 问题。
附上简单的测试代码:
static class TestCallable implements Callable<String> {
private static Random random = new Random(System.currentTimeMillis());
private int id;
private String strType;
TestCallable(String strType, int id) {
this.strType = strType;
this.id = id;
}
public String call() throws Exception {
String strThreadName = String.format("%s[%d] in \"%s\"", strType, id, Thread.currentThread().getName());
int sleepTime = 1000 + random.nextInt(1000);
log.info("TestCallable, strType={}, will sleep {} ms", strThreadName, sleepTime);
Thread.sleep(sleepTime);
return strThreadName;
}
}
@Test
public void testExecutor() throws Exception {
int cpuNums = Runtime.getRuntime().availableProcessors(); //获取当前系统的CPU 数目
final int TASK_COUNT = cpuNums * 2;
log.info("availableProcessors = {}", cpuNums);
//newCachedThreadPool, newFixedThreadPool
ThreadPoolExecutor executor = new ThreadPoolExecutor(0, cpuNums,
0L, TimeUnit.MILLISECONDS,
//通过设置 capacity 的值,从日志可以发现Java线程池的问题 -- 队列不满时,即使压了再多的任务,也不会创建新的线程
new LinkedBlockingQueue(Integer.MAX_VALUE)); //cpuNums * 2
assertEquals(0, executor.getCorePoolSize());
assertEquals(cpuNums, executor.getMaximumPoolSize());
assertEquals(0, getThreadNumberOfThreadPoolExecutor(executor));
log.info("Before submit {} TestCallable", TASK_COUNT);
//创建 TASK_COUNT 个任务并执行
for (int i = 0; i < TASK_COUNT; i++) {
Future futureReturn = executor.submit(new TestCallable("Cached", i));
resultList.add(futureReturn);
}
log.info("After submit all TestCallable");
//assertEquals(1, getThreadNumberOfThreadPoolExecutor(executor)); //由于队列未满,只会创建 coreSize 个线程,不会继续增加
for (Future future : resultList) {
log.info("Result: " + future.get());
assertTrue(future.isDone()); //get会阻塞等待任务结束
}
log.info("After get all TestCallable result");
运行结果如下(从日志中可以发现, 即使队列中已经有很多任务在等待,但线程池中只有一个线程在运行):
[main] INFO com.fishjam.java.ThreadTest - availableProcessors = 8
[main] INFO com.fishjam.java.ThreadTest - Before submit 16 TestCallable
[main] INFO com.fishjam.java.ThreadTest - After submit all TestCallable
[pool-1-thread-1] INFO com.fishjam.java.ThreadTest - TestCallable, strType=Cached[0] in "pool-1-thread-1", will sleep 1186 ms
[main] INFO com.fishjam.java.ThreadTest - Result: Cached[0] in "pool-1-thread-1"
[pool-1-thread-1] INFO com.fishjam.java.ThreadTest - TestCallable, strType=Cached[1] in "pool-1-thread-1", will sleep 1763 ms
[main] INFO com.fishjam.java.ThreadTest - Result: Cached[1] in "pool-1-thread-1"
[pool-1-thread-1] INFO com.fishjam.java.ThreadTest - TestCallable, strType=Cached[2] in "pool-1-thread-1", will sleep 1326 ms
[main] INFO com.fishjam.java.ThreadTest - Result: Cached[2] in "pool-1-thread-1"
`