ExecutorService-在特定的时间限制内执行每个任务

时间:2018-11-07 19:23:17

标签: java multithreading concurrency executorservice executor

我正在创建一个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时尚未完成第十一项任务

我的目标是使每个任务最多执行两分钟,而不管列表中的任务数量如何,以及之前或之后有多少任务。

谢谢

3 个答案:

答案 0 :(得分:0)

您可以使用System.currentTimeMillis();花费时间。然后您添加最长时间,例如120_000毫秒。 等待时,减去当前时间。即您只等到达到最大时间即可。

答案 1 :(得分:0)

ExecutorService#invokeAll可能是这里的关键。

这个问题有点难以理解(也许甚至是因为您试图精确地描述它?;-)。)因此,我创建了一个示例,试图将其包裹住。即使这不是您想要的目标,也可以解释这与您的目标有多大不同,从而可以澄清问题,或者其他人可以给出更好的答案。

该示例将任务创建为stdin对象,并将其放入列表中。这样的列表可以传递给Callable。 (根据您的情况,您可以使用ExecutorService#invokeAllRunnable任务中创建这些实例)。创建了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");
    }

}

这表明

  • 立即开始的任务运行了2000ms
  • 必须等待其他人执行的任务也运行了2000ms(但总共是4000ms)
  • 花费太长时间的任务在5000毫秒后被取消

答案 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秒的时间限制(因为我们从后续任务的超时值中减去了先前任务的执行时间)

我找到了这个简单而优雅的解决方案-但是,当然,我很高兴听到任何人能够对该解决方案进行增强或发表评论。