例示java线程池coreSize、maxSize、queueCapacity和timeout的作用

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

Leave a Comment

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.