线程池执行器的maximumPoolSize是如何工作的?

10

我正在尝试理解ThreadPoolExecutor类。我已经阅读了这个答案和Javadoc。但是我的实验结果与那个描述不符:

我使用一个工厂初始化线程池以跟踪id。

int tcounter = 0;
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 1, TimeUnit.MINUTES,
        new ArrayBlockingQueue<Runnable>(1000), new ThreadFactory() {

            @Override
            public Thread newThread(Runnable r) {
                return new mThread(tcounter++, r);
            }
        });

public class mThread extends Thread {
    int id;

    private mThread(int id, Runnable run) {
        super(run);
        GLog.e("created thread " + id);
        this.id = id;
    }

}

然后是任务:
public class mRunanble implements Runnable {
    int value = 0;

    private mRunanble(int value) {
        super();
        this.value = value;
    }

    @Override
    public void run() {
        SystemClock.sleep(3000);
        Thread t = Thread.currentThread();
        if (t instanceof mThread) {

            GLog.e("Say " + (value) + " on thread " + ((mThread) t).id);
        } 

    }

}

并将操作分配给按钮:

executor.execute(new mRunanble(i++));

但是我一直按那个按钮,第三个线程从未被创建,那么在ThreadPoolExecutor构造函数中的第二个参数(maximumPoolSize=4)是用来干什么的呢?我本来期望会创建4个线程,并且在执行结束后1分钟内会杀死其中的2个线程。


是因为你的线程正在休眠吗?也许将你的可运行对象更改为忙等待会给你期望的结果。 - happymeal
3个回答

7
ThreadPoolExecutor API中可以看出:
如果正在运行的线程数量多于corePoolSize但少于maximumPoolSize,则只有在队列已满的情况下才会创建新线程。
由于你的队列容量为1000,因此队列永远不会填满。如果将容量更改为1,则会看到创建ThreadExecutors类在其newCachedThreadPool方法中使用SynchronousQueue,因此您可能也希望考虑使用它。

如果我减小这个值,我会得到一个java.util.concurrent.RejectedExecutionException异常。 - Addev
1
一旦您减少了数量,它就会达到maxPoolSize限制,然后拒绝任务。因此,您可以在此异常之前打印池大小,该大小将等于maxpoolsize。 - amicngh
ThreadPoolExecutor在Android中的工作方式与此相同,文档还指出:如果正在运行的线程少于maximumPoolSize,则只有在队列已满的情况下才会创建一个新的线程来处理请求。 - bulletProofCat

2

那么,当我点击按钮时,应该创建额外的2个线程,直到达到最大线程池大小,对吗? - Addev
3
+1表示同意或赞成。举一个例子,线程池的初始大小为1,核心池大小为5,最大池大小为10,队列长度为100。Sun公司的做法是:当请求到达时,会创建线程直到达到5个,然后任务会被添加到队列中直到队列长度达到100。当队列已满时,新的线程将被创建直到达到最大池大小。一旦所有线程都在使用并且队列已满,新的任务将被拒绝。随着队列长度的缩小,活动线程的数量也会减少。 - Tushar Pandey

1

要使线程池创建新的附加线程(根据maximumPoolSize参数扩展池大小),请尝试执行此简单示例:

public class Main {

    public static void main(String[] args) throws InterruptedException {

        ThreadPoolExecutor tpe = new ThreadPoolExecutor(
                1, 2, 500, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(1));
        System.out.println("init pool size= " + tpe.getPoolSize() + ", queue size=" + tpe.getQueue().size());

        tpe.execute(new Task("1st", 10000));
        Thread.sleep(1000);
        print(tpe, "1st");

        tpe.execute(new Task("2nd", 0));
        Thread.sleep(1000);
        print(tpe, "2nd");

        tpe.execute(new Task("3d", 2000));
        Thread.sleep(1000);
        print(tpe, "3d");

        while (tpe.getPoolSize()>1) {           
            Thread.sleep(100);
        }
        System.out.println("pool size= " + tpe.getPoolSize() + ", queue size=" + tpe.getQueue().size());
        tpe.shutdown();
    }

    private static void print(ThreadPoolExecutor tpe, String name) {
        System.out.println("After " + name + " execute -  pool size= " + tpe.getPoolSize() + ", queue=" + tpe.getQueue());
    }

    private static class Task implements Runnable {

        private final String name;
        private final long time;

        Task(String name, long time) {
            this.name = name;
            this.time = time;
        }

        @Override
        public void run() {
            System.out.println("Run " + Thread.currentThread().getName() + "-" + name);
            try {
                Thread.sleep(time);
            } catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
            }
            System.out.println("Finish " + Thread.currentThread().getName() + "-" + name);
        }

        @Override
        public String toString() {
            return name;
        }

    }
}

你将得到输出结果,该结果展示了maximumPoolSizekeepAliveTime的影响:
init pool size= 0, queue size=0
Run pool-1-thread-1-1st
After 1st execute -  pool size= 1, queue=[]
After 2nd execute -  pool size= 1, queue=[2nd]
Run pool-1-thread-2-3d
After 3d execute -  pool size= 2, queue=[2nd]
Finish pool-1-thread-2-3d
Run pool-1-thread-2-2nd
Finish pool-1-thread-2-2nd
pool size= 1, queue size=0
Finish pool-1-thread-1-1st

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接