我处理了一个具有以下要求的线程应用程序的设计:它必须具有基于一天中的时间(峰值/非峰值)运行的动态线程数。
我完成了我的作业并研究了最好的方法,我发现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);
}
}
但是,查看日志,执行的线程数保持不变,没有变化。
答案 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
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之前重置所有内容