我正在创建一个ExecutorService
来执行一些任务,这些任务在正常情况下预计需要一分钟才能完成,但是在任何情况下,从任务执行之日起,运行时间不得超过两分钟开始。
我的代码如下:
ExecutorService executorService = Executors.newFixedThreadPool(10);
ArrayList<Future<?>> futuresList = new ArrayList<Future<?>>();
for (String singleTask: taskList) {
futuresList.add(executorService.submit( new Runnable(){
@Override
public void run(){
try {
performTask(p1, singleTask, p3);
} catch (IOException | InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}));
}
for(Future<?> future : futures) {
future.get(120, TimeUnit.SECONDS);
}
这将阻塞直到指定的超时,然后继续。我的问题如下:
1)如果task1
阻止了两分钟,而task2
也阻止了两分钟-则task2
将被“阻止”了总共4分钟(因为{{1}在future.get(120, TimeUnit.SECONDS);
完成阻止之前,不会在task2上调用})-即使两个任务都同时提交并开始执行
2)如果我提交的任务多于10个,则任务11+可能永远不会在所需的时间段内阻塞,如果先前的任务在调用task1
时尚未完成第十一项任务
我的目标是使每个任务最多执行两分钟,而不管列表中的任务数量如何,以及之前或之后有多少任务。
谢谢
答案 0 :(得分:0)
您可以使用System.currentTimeMillis();花费时间。然后您添加最长时间,例如120_000毫秒。 等待时,减去当前时间。即您只等到达到最大时间即可。
答案 1 :(得分:0)
ExecutorService#invokeAll
可能是这里的关键。
这个问题有点难以理解(也许甚至是因为您试图精确地描述它?;-)。)因此,我创建了一个示例,试图将其包裹住。即使这不是您想要的目标,也可以解释这与您的目标有多大不同,从而可以澄清问题,或者其他人可以给出更好的答案。
该示例将任务创建为stdin
对象,并将其放入列表中。这样的列表可以传递给Callable
。 (根据您的情况,您可以使用ExecutorService#invokeAll
从Runnable
任务中创建这些实例)。创建了5个任务。默认情况下,每个任务需要2000毫秒执行。任务Executors#callable
是单数任务,耗时8000ms。最大执行时间应为5000ms。
"C"
最后打印的摘要显示以下结果:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class ExecutorServiceLimitTaskTime
{
private static Map<String, Long> taskSubmitMs =
new ConcurrentHashMap<String, Long>();
private static Map<String, Long> taskStartMs =
new ConcurrentHashMap<String, Long>();
private static Map<String, Long> taskFinishedMs =
new ConcurrentHashMap<String, Long>();
public static void main(String[] args) throws Exception
{
ExecutorService executorService = Executors.newFixedThreadPool(3);
List<String> tasks = Arrays.asList("A", "B", "C", "D", "E");
List<Callable<String>> callables = new ArrayList<Callable<String>>();
for (String task : tasks)
{
taskSubmitMs.put(task, System.currentTimeMillis());
callables.add(new Callable<String>()
{
@Override
public String call()
{
taskStartMs.put(task, System.currentTimeMillis());
long durationMs = 2000;
if (task.equals("C"))
{
durationMs = 8000;
}
performTask(task, durationMs);
if (!Thread.currentThread().isInterrupted())
{
taskFinishedMs.put(task, System.currentTimeMillis());
}
return task;
}
});
}
List<Future<String>> futures =
executorService.invokeAll(callables, 5000, TimeUnit.MILLISECONDS);
for (Future<String> future : futures)
{
try
{
future.get();
}
catch (CancellationException e)
{
System.out.println("One task was cancelled");
}
}
for (String task : tasks)
{
Long submitMs = taskSubmitMs.get(task);
Long startMs = taskStartMs.get(task);
Long finishedMs = taskFinishedMs.get(task);
if (finishedMs != null)
{
long waitMs = startMs - submitMs;
long runMs = finishedMs - startMs;
long totalMs = finishedMs - submitMs;
System.out.printf(
"Task %-3s waited %5d ms and ran %5d ms, total %5d ms\n",
task, waitMs, runMs, totalMs);
}
else
{
System.out.printf(
"Task %-3s was cancelled\n", task);
}
}
}
private static void performTask(String task, long durationMs)
{
System.out.println("Executing " + task);
try
{
Thread.sleep(durationMs);
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
}
System.out.println("Executing " + task + " DONE");
}
}
这表明
答案 2 :(得分:0)
好的,我不确定我的问题是否被完全理解,但是我将尝试用自己想出的解决方案回答我自己的问题(这可能有助于向其他人澄清这个问题)。我认为@Peter Lawrey找不到这个答案,但是答案太短了,无法确定。
int timeLimitOfIndividualTaskInSeconds = 120;
int fixedThreadPoolCount = 10;
ExecutorService executorService = Executors.newFixedThreadPool(fixedThreadPoolCount);
ArrayList<Future<?>> futuresList = new ArrayList<Future<?>>();
for (String singleTask: taskList) {
futuresList.add(executorService.submit( new Runnable(){
@Override
public void run(){
try {
executeTask(singleTask);
} catch (IOException | InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}));
}
long beforeTimeInMilli = System.currentTimeMillis();
long beforeTimeInSeconds = TimeUnit.MILLISECONDS.toSeconds(beforeTimeInMilli);
int counter = 0;
long timeoutInSeconds = timeLimitOfIndividualTaskInSeconds;
for(Future<?> future : futuresList) {
if (counter % fixedThreadPoolCount == 0) {
// resets time limit to initial limit since next batch of tasks are beginning to execute
timeoutInSeconds = timeLimitOfIndividualTaskInSeconds;
}
try {
future.get(timeoutInSeconds, TimeUnit.SECONDS);
} catch (Exception e){
e.printStackTrace();
future.cancel(true); //stops the underlying task
}
counter++;
long afterTimeInMilli = System.currentTimeMillis();
long afterTimeInSeconds = TimeUnit.MILLISECONDS.toSeconds(afterTimeInMilli);
long taskDurationInSeconds = afterTimeInSeconds - beforeTimeInSeconds;
timeoutInSeconds = timeoutInSeconds - taskDurationInSeconds;
}
这保证了两件事:
1) 和已同时提交并开始执行的所有任务(即“同一批”)将以 max 运行 em> 120秒(但是如果任何任务在120秒之前完成,它将不会继续阻止)
2)同一批中的先前任务不会导致该批中的后续任务执行超过120秒的时间限制(因为我们从后续任务的超时值中减去了先前任务的执行时间)
我找到了这个简单而优雅的解决方案-但是,当然,我很高兴听到任何人能够对该解决方案进行增强或发表评论。