ThreadPoolExecutor没有正确收缩

时间:2015-02-17 17:19:57

标签: java multithreading threadpoolexecutor

我处理了一个具有以下要求的线程应用程序的设计:它必须具有基于一天中的时间(峰值/非峰值)运行的动态线程数。

我完成了我的作业并研究了最好的方法,我发现java有一个名为ThreadPoolExecutor的类: java.util.concurrent.ThreadPoolExecutor.ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)

这里关注的两个变量是corePoolSize和maximumPoolSize,它们都与workQueue一起充当线程池的下限和上限。调整这些值有不同的策略,建议使用执行器工厂方法而不是构造函数,以防这些参数不需要显式设置。

public class Main {
    public static void main(String[] args) {
        final BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(100);
        final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(0, 10, 0L, TimeUnit.MILLISECONDS, queue);
        threadPool.setRejectedExecutionHandler(new RejectedExecutionHandler() {
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                try {
                    executor.getQueue().put(r);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                threadPool.setCorePoolSize(1);
                threadPool.setMaximumPoolSize(1);
                System.out.println("changed");
            }
        };
        new Timer().schedule(task, 10000);

        for (int i = 0; i < 400; i++) {
            threadPool.submit(new WorkItem(i));
        }
    }
}

这是类似于要运行的线程的类

public class WorkItem implements Runnable {
    private int workItemNumber;
    private long startTime;

    public WorkItem(int workItemNumber) {
        this.workItemNumber = workItemNumber;
    }

    @Override
    public void run() {
        startTime = System.currentTimeMillis();
        System.out.println("thread Number: " + workItemNumber + " started at: " + startTime);
        while (System.currentTimeMillis() - startTime < 5000) {
        }
        System.out.println("WorkItem done: " + workItemNumber);
    }
}

但是,查看日志,执行的线程数保持不变,没有变化。

4 个答案:

答案 0 :(得分:1)

您已创建了一个包含10个最大线程的池

new ThreadPoolExecutor(0, 10, 0L, TimeUnit.MILLISECONDS, queue);

您已提交了400项任务

for (int i = 0; i < 400; i++) {
    threadPool.submit(new Thread(System.currentTimeMillis(), i));
}

线程池不会使用超过10个线程(由java.lang.Thread类表示的线程)来执行您的任务。

提交和执行所有这些任务所花费的时间少于您为TimerTask

设置的10000毫秒延迟
new Timer().schedule(task, 10000, 5000);

运行TimerTask后,您的池只会运行一个线程并声明已提交的任务(一旦完成其他线程&#39;任务)。


示例表明,ThreadPoolExecutor执行完TimerTask后,public class Jackson { public static void main(String[] args) { final BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(100); final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(0, 10, 0L, TimeUnit.MILLISECONDS, queue); threadPool.setRejectedExecutionHandler(new RejectedExecutionHandler() { public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { try { executor.getQueue().put(r); } catch (InterruptedException e) { e.printStackTrace(); } } }); TimerTask task = new TimerTask() { @Override public void run() { threadPool.setCorePoolSize(1); threadPool.setMaximumPoolSize(1); System.out.println("changed"); this.cancel(); } }; new Timer().schedule(task, 5, 5000); for (int i = 0; i < 400; i++) { threadPool.submit(new WorkItem(i)); } } } class WorkItem implements Runnable { private int workItemNumber; public WorkItem(int workItemNumber) { this.workItemNumber = workItemNumber; } @Override public void run() { System.out.println("WorkItem #" + workItemNumber + " executing on Thread with name: " + Thread.currentThread().getName()); } } 只剩下一个线程

{{1}}

答案 1 :(得分:1)

您的代码完全按照您的意图运行。启动并运行10个线程,排队100个线程。此时,阻塞队列阻止了您的主线程(一个排队线程)。然后,您的计时器将可用线程更改为1,这意味着您的队列处理速度更慢。然而,您所看到的是,因为您的线程必须等待超过10秒才能实际执行它们立即完成。尝试对您的代码进行以下更改:

public class WorkItem implements Runnable {
    private long startTime;
    private long runTime;
    private int workItemNumber;

    public WorkItem(long startTime, int workItemNumber) {
        this.startTime = startTime;
        this.workItemNumber= workItemNumber;
    }

    @Override
    public void run() {
        System.out.println("WorkItem started: " + workItemNumber + " Queued at: " + startTime);
        runTime = System.currentTimeMillis();
        while (System.currentTimeMillis() - runTime < 10000) {
        }
        System.out.println("WorkItem done: " + workItemNumber);
    }
}

这将让您看到正如您所期望的那样执行。使用核心池设置为0的阵列阻塞队列的奇怪之处在于它只启动单个线程,然后填满队列,然后启动更多线程(最大池大小)。如果您对排队代码进行细微更改,就会发现这种情况。

for (int i = 1; i < 101; i++) {
    threadPool.submit(new WorkItem(System.currentTimeMillis(), i));
}

for (int i = 101; i < 401; i++) {
    long thisTime = System.currentTimeMillis();
    threadPool.submit(new WorkItem(System.currentTimeMillis(), i));
    while (System.currentTimeMillis() - thisTime < 500) {

    }
}

答案 2 :(得分:0)

看起来“设置最大池大小”方法只会减少空闲线程数...

public void setMaximumPoolSize(int maximumPoolSize) {
    if (maximumPoolSize <= 0 || maximumPoolSize < corePoolSize)
        throw new IllegalArgumentException();
    this.maximumPoolSize = maximumPoolSize;
    if (workerCountOf(ctl.get()) > maximumPoolSize)
        interruptIdleWorkers();
}

如果线程保持忙碌,则看起来它们不会被释放。

(我可能错了......当一个线程完成清理时,它看起来并不像任何神奇的事情 - 需要多看一眼......)

答案 3 :(得分:0)

虽然在更高版本的JDK / Grails设置max poolSize上使用它可以减少它在旧版本的grails和JDK7中运行良好。 (不确定问题所在的位置我必须这样做)

private static final int actualPoolSize =  Holders.grailsApplication.config.maximumPoolSize ?: 3
    private static int maxPoolSize = actualPoolSize
public EnhancedExecutor() {
        super(maxPoolSize,maxPoolSize,keepAliveTime,timeoutUnit,new PriorityBlockingQueue<Runnable>(maxQueue))
    }
public void setMaxPoolSize(int i) {
        this.maxPoolSize=i
        super.purge()
        super.setCorePoolSize(i?:actualPoolSize)        
        super.setMaximumPoolSize(i?:actualPoolSize)
    }

没有清除,我可以增加到更高的水平,没有任何错误。尝试减少为i返回null。或actualPoolSize。 (似乎不想在没有抛出异常的情况下收缩)。

我接受了BretC关于线程忙碌的评论,看来purge解决了这个问题,确保在尝试重置superValues之前重置所有内容