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下都能得到我们想要的结果。