这是使用 java.util.concurrent.FutureTask 的好方法吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/536327/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 15:57:03  来源:igfitidea点击:

Is it a good way to use java.util.concurrent.FutureTask?

javamultithreadingconcurrencyfuturetask

提问by Romain Linsolas

First of all, I must say that I am quite new to the API java.util.concurrent, so maybe what I am doing is completely wrong.

首先,我必须说我对 API java.util.concurrent 很陌生,所以也许我所做的完全错误。

What do I want to do?

我想做什么?

I have a Java application that basically runs 2 separate processing (called myFirstProcess, mySecondProcess), but these processing must be run at the same time.

我有一个 Java 应用程序,它基本上运行 2 个单独的处理(称为myFirstProcessmySecondProcess),但这些处理必须同时运行。

So, I tried to do that:

所以,我试图这样做:

public void startMyApplication() {
    ExecutorService executor = Executors.newFixedThreadPool(2);
    FutureTask<Object> futureOne = new FutureTask<Object>(myFirstProcess);
    FutureTask<Object> futureTwo = new FutureTask<Object>(mySecondProcess);
    executor.execute(futureOne);
    executor.execute(futureTwo);
    while (!(futureOne.isDone() && futureTwo.isDone())) {
        try {
            // I wait until both processes are finished.
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    logger.info("Processing finished");
    executor.shutdown();
    // Do some processing on results
    ...
}

myFirstProcessand mySecondProcessare classes that implements Callable<Object>, and where all their processing is made in the call() method.

myFirstProcessmySecondProcess是实现 的类,Callable<Object>它们的所有处理都在 call() 方法中进行。

It is working quite well but I am not sure that it is the correct way to do that. Is a good way to do what I want? If not, can you give me some hints to enhance my code (and still keep it as simple as possible).

它工作得很好,但我不确定这是正确的方法。是做我想做的好方法吗?如果没有,您能否给我一些提示以增强我的代码(并仍然尽可能简单)。

采纳答案by Yuval Adam

You'd be better off using the get()method.

你最好使用这个get()方法。

futureOne.get();
futureTwo.get();

Both of which wait for notification from the thread that it finished processing, this saves you the busy-wait-with-timer you are now using which is not efficient nor elegant.

两者都等待来自它完成处理的线程的通知,这为您节省了您现在正在使用的忙等待计时器,这既不高效也不优雅。

As a bonus, you have the API get(long timeout, TimeUnit unit)which allows you to define a maximum time for the thread to sleep and wait for a response, and otherwise continues running.

作为奖励,您拥有 API get(long timeout, TimeUnit unit),它允许您定义线程休眠和等待响应的最长时间,否则继续运行。

See the Java APIfor more info.

有关更多信息,请参阅Java API

回答by Louis Jacomet

You may want to use a CyclicBarrierif you are interested in starting the threads at the same time, or waiting for them to finish and then do some further processing. See the javadoc for more information.

如果您有兴趣同时启动线程,或者等待它们完成然后做一些进一步的处理,您可能想要使用CyclicBarrier。有关更多信息,请参阅 javadoc。

回答by cletus

Yuval's solution is fine. As an alternative you can also do this:

Yuval 的解决方案很好。作为替代方案,您也可以这样做:

ExecutorService executor = Executors.newFixedThreadPool();
FutureTask<Object> futureOne = new FutureTask<Object>(myFirstProcess);
FutureTask<Object> futureTwo = new FutureTask<Object>(mySecondProcess);
executor.execute(futureOne);
executor.execute(futureTwo);
executor.shutdown();
try {
  executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
  // interrupted
}

What is the advantage of this approach? There's not a lot of difference really except that this way you stop the executor accepting any more tasks (you can do that the other way too). I tend to prefer this idiom to that one though.

这种方法的优点是什么?除了通过这种方式阻止执行程序接受更多任务(您也可以通过其他方式执行此操作)之外,实际上并没有太大区别。不过,我倾向于更喜欢这个习语而不是那个习语。

Also, if either get() throws an exception you may end up in a part of your code that assumes both tasks are done, which might be bad.

此外,如果 get() 抛出异常,您最终可能会在假设两个任务都已完成的部分代码中结束,这可能很糟糕。

回答by seh

The uses of FutureTaskabove are tolerable, but definitely not idiomatic. You're actually wrapping an extraFutureTaskaround the one you submitted to the ExecutorService. Your FutureTaskis treated as a Runnableby the ExecutorService. Internally, it wraps your FutureTask-as-Runnablein a new FutureTaskand returns it to you as a Future<?>.

FutureTask以上的用法是可以接受的,但绝对不是惯用的。你在实际包装的额外FutureTask你身边提交的一个ExecutorService。你FutureTask被视为一个Runnable通过ExecutorService。在内部,它将您的FutureTask-as-包装成Runnable一个新的FutureTask,并将其作为Future<?>.

Instead, you should submit your Callable<Object>instances to a CompletionService. You drop two Callables in via submit(Callable<V>), then turn around and call CompletionService#take()twice (once for each submitted Callable). Those calls will block until one and then the other submitted tasks are complete.

相反,您应该将您的Callable<Object>实例提交到CompletionService. 您将两个Callables 放入 via submit(Callable<V>),然后转身并调用CompletionService#take()两次(每次提交一次Callable)。这些调用将阻塞,直到一个,然后其他提交的任务完成。

Given that you already have an Executorin hand, construct a new ExecutorCompletionServicearound it and drop your tasks in there. Don't spin and sleep waiting; CompletionService#take()will block until either one of your tasks are complete (either finished running or canceled) or the thread waiting on take()is interrupted.

鉴于您已经拥有一个ExecutorExecutorCompletionService围绕它构建一个新的并将您的任务放在那里。不要旋转和睡觉等待;CompletionService#take()将阻塞,直到您的任务之一完成(完成运行或取消)或等待的线程take()被中断。

回答by HakunaMatata

You can use invokeall(Colelction....) method

您可以使用 invokeall(Colelction....) 方法

package concurrent.threadPool;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class InvokeAll {

    public static void main(String[] args) throws Exception {
        ExecutorService service = Executors.newFixedThreadPool(5);
        List<Future<java.lang.String>> futureList = service.invokeAll(Arrays.asList(new Task1<String>(),new Task2<String>()));

        System.out.println(futureList.get(1).get());
        System.out.println(futureList.get(0).get());
    }

    private static class Task1<String> implements Callable<String>{

        @Override
        public String call() throws Exception {
            Thread.sleep(1000 * 10);
            return (String) "1000 * 5";
        }

    }

    private static class Task2<String> implements Callable<String>{

        @Override
        public String call() throws Exception {
            Thread.sleep(1000 * 2);
            int i=3;
            if(i==3)
                throw new RuntimeException("Its Wrong");
            return (String) "1000 * 2";
        }

    }
}

回答by Anderson

If your futureTasks are more then 2, please consider [ListenableFuture][1].

如果您的 futureTasks 超过 2,请考虑[ListenableFuture][1].

When several operations should begin as soon as another operation starts -- "fan-out" -- ListenableFuture just works: it triggers all of the requested callbacks. With slightly more work, we can "fan-in," or trigger a ListenableFuture to get computed as soon as several other futures have all finished.

当几个操作应该在另一个操作开始时立即开始时——“扇出”——ListenableFuture 正常工作:它触发所有请求的回调。稍微多做一点工作,我们就可以“扇入”,或者其他几个期货都完成后立即触发 ListenableFuture 进行计算。