简体   繁体   English

Java并行执行多个任务超时

[英]Java timeout multiple tasks in parallel

What is the best practice approach to launch a pool of 1000's of tasks (where up to 4 should be able to execute in parallel) and automatically timeout them if they take more than 3 seconds (individually)? 什么是启动1000个任务池(最多可以并行执行4个任务)并在它们(分别)花费3秒以上时自动使它们超时的最佳实践方法是什么?

While I found that ExecutorService seems to be helpful (see SSCE from another post below), I don't see how to make this work for multiple tasks running in parallel (as the future.get(3, TimeUnit.SECONDS) is executing on the same thread than the one launching the tasks, hence no opportunity to launch multiple tasks in parallel): 尽管我发现ExecutorService似乎很有帮助(请参阅下面的另一篇文章的SSCE),但我看不到如何使并行运行的多个任务有效(因为future.get(3, TimeUnit.SECONDS)正在执行与启动任务的线程相同,因此没有机会并行启动多个任务):

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class Test {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<String> future = executor.submit(new Task());

        try {
            System.out.println("Started..");
            System.out.println(future.get(3, TimeUnit.SECONDS));
            System.out.println("Finished!");
        } catch (TimeoutException e) {
            future.cancel(true);
            System.out.println("Terminated!");
        }

        executor.shutdownNow();
    }
}

class Task implements Callable<String> {
    @Override
    public String call() throws Exception {
        Thread.sleep(4000); // Just to demo a long running task of 4 seconds.
        return "Ready!";
    }
}

Thanks! 谢谢!

If you have to monitor each task to kill it when it exceeds the timeout period, either 如果您必须监视每个任务以在超过超时期限时将其杀死,则可以选择

  1. the task itself has to keep track of time and quit appropriately, OR 任务本身必须跟踪时间并适当退出,或者
  2. you have to create a second watchdog thread for every task. 您必须为每个任务创建第二个看门狗线程。 The watchdog thread sets a timer and sleeps, waking up after the timeout interval expires and then terminating the task if it's still running. 看门狗线程设置了一个计时器并进入睡眠状态,在超时间隔到期后醒来,如果该任务仍在运行,则终止该任务。

This is a tricky one. 这是一个棘手的问题。 Here's what I came up with: 这是我想出的:

public class TaskQueue<T> {
    private static final Logger logger =
        Logger.getLogger(TaskQueue.class.getName());

    private final Collection<Callable<T>> tasks;

    private final int maxTasks;

    private int addsPending;

    private final Collection<T> results = new ArrayList<T>();

    private final ScheduledExecutorService executor;

    public TaskQueue() {
        this(4);
    }

    public TaskQueue(int maxSimultaneousTasks) {
        maxTasks = maxSimultaneousTasks;
        tasks = new ArrayDeque<>(maxTasks);
        executor = Executors.newScheduledThreadPool(maxTasks * 3);
    }

    private void addWhenAllowed(Callable<T> task)
    throws InterruptedException,
           ExecutionException {

        synchronized (tasks) {
            while (tasks.size() >= maxTasks) {
                tasks.wait();
            }
            tasks.add(task);

            if (--addsPending <= 0) {
                tasks.notifyAll();
            }
        }

        Future<T> future = executor.submit(task);
        executor.schedule(() -> future.cancel(true), 3, TimeUnit.SECONDS);
        try {
            T result = future.get();
            synchronized (tasks) {
                results.add(result);
            }
        } catch (CancellationException e) {
            logger.log(Level.FINE, "Canceled", e);
        } finally {
            synchronized (tasks) {
                tasks.remove(task);
                if (tasks.isEmpty()) {
                    tasks.notifyAll();
                }
            }
        }
    }

    public void add(Callable<T> task) {
        synchronized (tasks) {
            addsPending++;
        }

        executor.submit(new Callable<Void>() {
            @Override
            public Void call()
            throws InterruptedException,
                   ExecutionException {
                addWhenAllowed(task);
                return null;
            }
        });
    }

    public Collection<T> getAllResults()
    throws InterruptedException {
        synchronized (tasks) {
            while (addsPending > 0 || !tasks.isEmpty()) {
                tasks.wait();
            }
            return new ArrayList<T>(results);
        }
    }

    public void shutdown() {
        executor.shutdown();
    }
}

I suspect it could be done more cleanly using Locks and Conditions instead of synchronization. 我怀疑可以使用锁和条件(而不是同步)更干净地完成此操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM