Future是否需要在单独的线程中执行计算?

时间:2015-10-01 06:52:40

标签: java multithreading future

来自documentation

  

Future表示异步计算的结果。

这是否意味着调用方法Future#get的线程不应该是执行计算的线程?那么,如果调用Future#get的线程尚未启动计算,它是否合适?我不确定我是否可以将其称为异步计算...

3 个答案:

答案 0 :(得分:4)

术语“异步计算”并不强制要求计算必须在不同的线程中运行。如果API设计者打算这样做,他们就写了“在不同线程中运行的计算”。在这里,它只是意味着没有关于计算何时发生的规范。

同样,JRE提供的现有实现不会在不同的线程中强制执行计算。最可能的实现,FutureTask可以如下使用:

Callable<String> action = new Callable<String>() {
    public String call() {
        return "hello "+Thread.currentThread();
    }
};

FutureTask<String> ft=new FutureTask<>(action);
ft.run();
System.out.println(ft.get());

通常,FutureTask的实例是由ExecutorService创建的,它将确定计算执行的时间和步骤:

ExecutorService runInPlace=new AbstractExecutorService() {
    public void execute(Runnable command) {
        command.run();
    }
    public void shutdown() {}
    public List<Runnable> shutdownNow() { return Collections.emptyList(); }
    public boolean isShutdown() { return false; }
    public boolean isTerminated() { return false; }
    public boolean awaitTermination(long timeout, TimeUnit unit) { return false; }
};
Future<String> f=runInPlace.submit(action);
System.out.println(ft.get());

请注意,此execute()实施不违反its contract

  

将来某个时间执行给定的命令。该命令可以在Executor实现的判断下在新线程,池化线程或调用线程中执行。

注意“或在调用线程中”......

另一个实现是ForkJoinTask

ForkJoinTask<String> fjt=new RecursiveTask<String>() {
    protected String compute() {
        return "hello "+Thread.currentThread();
    }
};
fjt.invoke();
System.out.println(fjt.get());

请注意,虽然此类任务旨在支持拆分为可由不同线程执行的子任务,但在此处使用调用线程故意。如果任务无法拆分,它将完全在调用者的线程中运行,因为这是最有效的解决方案。

这些示例都在调用程序线程中运行,但它们都不会执行get()方法中的任务。原则上,当执行计算时,它不会违反合同,因为它返回结果值,但是,在尝试正确实现get(long timeout, TimeUnit unit)时,您可能会遇到麻烦。相反,不工作cancel()仍然在合同范围内。

答案 1 :(得分:1)

Future.get()可以使用执行计算的相同线程完成,但最终结果是没有任何内容同时完成。当第一个执行器被取消注释而第二个执行器被注释掉时,在下面的代码中进行了演示。

可以使用扩展FutureTask(还有其他选项,如RejectedExecutionHandler),以便在计算时使用当前线程执行计算(下面代码中为Callable)不能同时完成。

关于这是否合适:在某些特殊情况下,可能需要这样做但不是如何使用它。对我来说,它看起来像是一个过早的优化,如果我能看到(测量)遵循预期用途的代码不能提供所需的性能,并且专用/优化的代码显示出显着的性能改进(并满足要求的表现)。

import java.util.concurrent.*;

public class FutureGetSameThread {

    public static void main(String[] args) {

        // Serial executor with a task-queue 
        // ExecutorService executor = Executors.newSingleThreadExecutor();
        // Serial executor without a task-queue 
        ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
        try {
            Callable<Integer> c = new Callable<Integer>() {
                @Override 
                public Integer call() throws Exception {
                    Thread.sleep(400L); // pretend to be busy
                    return 1;
                }
            };
            final Future<Integer> f = executor.submit(c);
            Callable<Integer> c2 = new Callable<Integer>() {
                @Override 
                public Integer call() throws Exception {
                    // wait for the result from the previous callable and add 1
                    return f.get() + 1;
                }
            };
            Future<Integer> f2 = null;
            try {
                f2 = executor.submit(c2);
                System.out.println("Second callable accepted by executor with task queue.");
            } catch (RejectedExecutionException ree) {
                System.out.println("Second callable rejected by executor without task queue.");
                f2 = new FutureTaskUsingCurrentThread<Integer>(c2);
            }
            Integer result = f2.get();
            System.out.println("Result: " + result);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            executor.shutdownNow();
        }
    }

    static class FutureTaskUsingCurrentThread<V> extends FutureTask<V> {

        public FutureTaskUsingCurrentThread(Callable<V> callable) {
            super(callable);
        }

        @Override
        public V get() throws InterruptedException, ExecutionException {
            run();
            return super.get();
        }
    }

}

答案 2 :(得分:1)

从技术上讲,java.util.concurrent.Future只是interface

它是一个可能无法立即获得的价值的占位符。可以访问Future的方法可以:

  • 测试值是否可用
  • 获取值,或
  • &#34;取消&#34;未来。

根据API合同,允许future.get()操作阻止调用者,直到该值可用。

API合约表示值来自哪里,或者它可能不可用的原因,或者什么机制可能会阻止get()方法的调用者;虽然它说&#34;取消&#34;如果出现表现出来,它就不会说什么&#34;取消&#34;实际应该这样做。这完全依赖于实现。

Future旨在由executorService.submit(callable)返回。

如果您的方法从其中一个内置ExecutorService实现获得Future,那么当您调用future.get()时,您实际上将等待另一个线程生成结果。