先来回顾下线程池的执行流程:
任务加入等待队列的条件是核心线程池已满,且等待队列未满。我原本以为这个核心线程池已满,指的是核心线程都在执行任务,但是我今天遇到一个现象,发现并不是我想的那样。
现象:在第一次执行业务时(创建的线程数大于核心线程数),在未关闭线程池的前提下,再次执行业务时(使用的线程不大于核心线程数),居然新建了新的线程来执行任务。
为了更好地重现问题,代码设计如下:
/**
* @Author shenxy
* @Date 2023/2/25 16:23
* @Version 1.0
*/
public class TestCallable {
//核心线程数
private static final int CORE_POOL_SIZE = 5;
//最大线程数
private static final int MAX_POOL_SIZE = 10;
//队列容量
private static final int QUEUE_CAPACITY = 1;
//等待时间
private static final Long KEEP_ALIVE_TIME = 1L;
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ALIVE_TIME,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(QUEUE_CAPACITY),
new ThreadPoolExecutor.CallerRunsPolicy());
List<Future<String>> futureList = new ArrayList<>();
//创建5个任务
for (int i = 0; i < 5; i++) {
Callable<String> callable = new MyCallable();
//提交任务到线程池
Future<String> future = executor.submit(callable);
//将返回值 future 添加到 list,我们可以通过 future 获得 执行 Callable 得到的返回值
futureList.add(future);
}
try {
//等待1秒,等第一次循环执行完
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//打印核心线程数与总线程数
System.out.println("当前核心线程数:"+executor.getCorePoolSize() + ",当前总线程数:" + executor.getPoolSize());
//创建5个任务
for (int i = 0; i < 5; i++) {
Callable<String> callable = new MyCallable();
//提交任务到线程池
Future<String> future = executor.submit(callable);
//将返回值 future 添加到 list,我们可以通过 future 获得 执行 Callable 得到的返回值
futureList.add(future);
}
//关闭线程池
executor.shutdown();
}
}
class MyCallable implements Callable {
@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName() + " 上床睡觉 " + new Date());
try {
//睡觉,
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 睡醒了 " + new Date());
return Thread.currentThread().getName();
}
}
执行结果如下:
可以看到,第一次循环任务执行完后线程池中有5个核心线程,第二次循环执行5个任务,线程池中空闲的核心线程数量是足够的,但是第二次循环还是新建了两个线程。
猜测当第二次循环时,虽然核心线程是空闲状态,任务还是会先加入等待队列中,然后再由核心线程执行。因为代码里等待队列长度设置的为1,所以当队列满时,再加入任务便会创建新的线程(即使此时核心线程是空闲状态)。
对代码做如下修改:
第二次循环加入任务时先等待50毫秒,发现没有创建新的线程。所以造成创建新线程的原因是任务加入的速度大于核心线程领取任务的速度,使等待队列满了。后面将等待队列长度由1改为5后,便不再出现该现象了。
总结,我们在设置等待队列容量时,应大于核心线程数。否则便会出现核心线程有空闲的情况下,还是会创建新的线程,且队列容量越小,出现的概率越大。
© 版权声明
THE END
暂无评论内容