java线程池的coreSize、maxSize、queueCapacity和timeout倒底有什么用?它们如何影响当前池内的线程数和队列里的任务数?
网上可以很容易搜到答案,
但为了增加感性认识,我们可以做个实验:
1. 令coreSize = 2, maxSize = 4
2. 令queue最大容量 = 6
3. 令超时时间 = 1 分钟
在看代码之前,可以先看下执行结果:
线程池已创建,但还没添加任务。当前池内线程数:0; 当前队列中元素数:0/6
添加了第1个任务。 当前池内线程数:1; 当前队列中元素数:0/6
添加了第2个任务。 当前池内线程数:2; 当前队列中元素数:0/6
添加了第3个任务。 当前池内线程数:2; 当前队列中元素数:1/6
添加了第4个任务。 当前池内线程数:2; 当前队列中元素数:2/6
添加了第5个任务。 当前池内线程数:2; 当前队列中元素数:3/6
添加了第6个任务。 当前池内线程数:2; 当前队列中元素数:4/6
添加了第7个任务。 当前池内线程数:2; 当前队列中元素数:5/6
添加了第8个任务。 当前池内线程数:2; 当前队列中元素数:6/6
添加了第9个任务。 当前池内线程数:3; 当前队列中元素数:6/6
添加了第10个任务。 当前池内线程数:4; 当前队列中元素数:6/6
过了30秒后,当前池内线程数:4; 当前队列中元素数:6/6
过了60秒后,当前池内线程数:4; 当前队列中元素数:2/6
过了90秒后,当前池内线程数:4; 当前队列中元素数:2/6
过了120秒后,当前池内线程数:4; 当前队列中元素数:0/6
过了150秒后,当前池内线程数:4; 当前队列中元素数:0/6
过了180秒后,当前池内线程数:2; 当前队列中元素数:0/6
过了210秒后,当前池内线程数:2; 当前队列中元素数:0/6
过了240秒后,当前池内线程数:0; 当前队列中元素数:0/6
可得结论:
1. 线程池刚创建、还没有任务进来时,线程数为0
2. 任务进来时,如果当前线程数未及coreSize,系统会创建新线程来处理
3. 任务进来时,如果当前线程数已经达到或超过了coreSize,系统会把任务置入队列
4. 如果队列里已经塞不下了,则系统会看一下当前线程数是否已达maxSize,如果还没达到,则系统创建新线程来处理(否则,会拒绝任务)
5. 如果当前线程数已经达到maxSize, 那么在队列清空以前,仍会有maxSize的线程在工作; 即使队列里只剩很少的任务
6. 如果当前已经不需要maxSize数的线程数,会有线程被杀死直到线程数=coreSize
7. coreSize数的线程空闲一段时间后会被杀死。
最后给出这个实验的
代码:
package player.kent.chen.learn.poolsize; import java.io.IOException; import java.text.MessageFormat; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class HelloPoolSize { private static final class OneMinuteTask implements Callable<Void> { public Void call() throws Exception { // System.err.println("\t" + Thread.currentThread().getName() // + " 's call() method has been invoked"); doSleep(1 * 60 * 1000); // System.err.println("\t" + Thread.currentThread().getName() // + " 's call() method invocation done"); return null; } }; public static void main(String[] args) throws IOException { int queueCapacity = 6; LinkedBlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(queueCapacity); ExecutorService executor = new ThreadPoolExecutor(2, 4, 1, TimeUnit.MINUTES, workQueue); System.out.print("线程池已创建,但还没添加任务。"); printThreadAndQueueCount(workQueue, queueCapacity); for (int i = 1; i <= 10; i++) { executor.submit(new OneMinuteTask()); System.out.print("添加了第" + i + "个任务。 "); doSleep(1 * 1000); printThreadAndQueueCount(workQueue, queueCapacity); } for (int i = 1; i < 1000000; i++) { int timePeriod = 30; doSleep(timePeriod * 1000); System.out.print("过了" + (i * timePeriod) + "秒后,"); printThreadAndQueueCount(workQueue, queueCapacity); } } private static void printThreadAndQueueCount(LinkedBlockingQueue<Runnable> workQueue, int queueCapacity) throws IOException { Set<Thread> threadSet = Thread.getAllStackTraces().keySet(); int threadCount = 0; for (Thread thread : threadSet) { if (thread.isDaemon() || thread.getName().equals("main")) { continue; } threadCount++; } System.out.println(MessageFormat.format("当前池内线程数:{0}; 当前队列中元素数:{1}/{2}", threadCount, workQueue.size(), queueCapacity)); } private static void doSleep(long millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { throw new RuntimeException(e); } } }
补充说明: 按照javadoc的描述,如果线程池的allowCoreThreadTimeOut == false, 则核心线程是不会被杀死的;但由于java 1.6实现上的bug,这个契约没有得到保障,核心线程一般还是会终结;据说java 1.7修正了这个问题。
为了避免空闲线程,以后我们把 allowCoreThreadTimeOut总是设成true好了;这样在1.6和1.7下都能得到我们想要的结果。