Future 是否需要在单独的线程中执行计算?
Does Future require to perform the computation in a separate thread?
Future represents the result of an asynchronous computation.
是不是说调用方法Future#get should not be a thread which performed the computation? So, is it appropriate if the thread calling Future#get的线程还没有开始计算呢?我不确定是否可以将其称为异步计算...
A Future.get()
可以使用执行计算的同一个线程来完成,但最终结果是没有任何事情是同时进行的。当第一个执行器被取消注释而第二个被注释掉时,在下面的代码中进行了演示。
扩展 FutureTask
can be used (there are other options like the RejectedExecutionHandler
) 使用当前线程执行计算,以防无法同时进行计算(下面代码中的 Callable
)。
至于这是否合适:在某些特殊情况下可能需要这样做,但这不是打算使用的方式。对我来说,这看起来像是一个过早的优化,只有当我看到(测量)遵循预期用途的代码没有提供所需的性能并且 specialized/optimized 代码显示出显着的性能改进时,我才会使用它(并且满足要求的性能)。
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();
}
}
}
从技术上讲,java.util.concurrent.Future
只是一个 interface
。
它是一个可能不会立即可用的值的占位符。可以访问 Future 的方法可以:
- 测试值是否可用,
- 获取值,或者
- "cancel"未来。
根据 API 合同,允许 future.get()
操作阻塞调用者,直到值可用。
API 合约 没有 说明价值来自哪里,或者为什么它可能不可用,或者什么机制可能会阻止 get()
方法;虽然它说 "cancel" 应该如何 出现 表现,但它并没有说明 "cancel" 实际上应该做什么。这完全取决于实现。
Future
本应由 executorService.submit(callable)
返回。
如果您的方法从内置 ExecutorService
实现之一获得 Future,那么当您调用 future.get()
时,您实际上将等待另一个线程产生结果。
术语“异步计算”并不强制要求计算必须 运行 在不同的线程中进行。如果 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:
Executes the given command at some time in the future. The command may execute in a new thread, in a pooled thread, or in the calling thread, at the discretion of the Executor implementation.
注意“或在调用线程中”…
另一个实现是 ForkJoinTask
:
ForkJoinTask<String> fjt=new RecursiveTask<String>() {
protected String compute() {
return "hello "+Thread.currentThread();
}
};
fjt.invoke();
System.out.println(fjt.get());
请注意,虽然此类任务旨在支持拆分为可由不同线程执行的子任务,但此处利用调用线程是 有意。如果无法拆分任务,它 运行 完全在调用者的线程中,因为这是最有效的解决方案。
这些示例全部 运行 在调用者线程中,但其中 none 将在 get()
方法中执行任务。原则上,这不会违反合同,因为它 returns 结果值,当计算已执行时,但是,在尝试正确实现 get(long timeout, TimeUnit unit)
时可能会遇到麻烦。相比之下,有一个不工作的 cancel()
仍然在合同范围内。
Future represents the result of an asynchronous computation.
是不是说调用方法Future#get should not be a thread which performed the computation? So, is it appropriate if the thread calling Future#get的线程还没有开始计算呢?我不确定是否可以将其称为异步计算...
A Future.get()
可以使用执行计算的同一个线程来完成,但最终结果是没有任何事情是同时进行的。当第一个执行器被取消注释而第二个被注释掉时,在下面的代码中进行了演示。
扩展 FutureTask
can be used (there are other options like the RejectedExecutionHandler
) 使用当前线程执行计算,以防无法同时进行计算(下面代码中的 Callable
)。
至于这是否合适:在某些特殊情况下可能需要这样做,但这不是打算使用的方式。对我来说,这看起来像是一个过早的优化,只有当我看到(测量)遵循预期用途的代码没有提供所需的性能并且 specialized/optimized 代码显示出显着的性能改进时,我才会使用它(并且满足要求的性能)。
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();
}
}
}
从技术上讲,java.util.concurrent.Future
只是一个 interface
。
它是一个可能不会立即可用的值的占位符。可以访问 Future 的方法可以:
- 测试值是否可用,
- 获取值,或者
- "cancel"未来。
根据 API 合同,允许 future.get()
操作阻塞调用者,直到值可用。
API 合约 没有 说明价值来自哪里,或者为什么它可能不可用,或者什么机制可能会阻止 get()
方法;虽然它说 "cancel" 应该如何 出现 表现,但它并没有说明 "cancel" 实际上应该做什么。这完全取决于实现。
Future
本应由 executorService.submit(callable)
返回。
如果您的方法从内置 ExecutorService
实现之一获得 Future,那么当您调用 future.get()
时,您实际上将等待另一个线程产生结果。
术语“异步计算”并不强制要求计算必须 运行 在不同的线程中进行。如果 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:
Executes the given command at some time in the future. The command may execute in a new thread, in a pooled thread, or in the calling thread, at the discretion of the Executor implementation.
注意“或在调用线程中”…
另一个实现是 ForkJoinTask
:
ForkJoinTask<String> fjt=new RecursiveTask<String>() {
protected String compute() {
return "hello "+Thread.currentThread();
}
};
fjt.invoke();
System.out.println(fjt.get());
请注意,虽然此类任务旨在支持拆分为可由不同线程执行的子任务,但此处利用调用线程是 有意。如果无法拆分任务,它 运行 完全在调用者的线程中,因为这是最有效的解决方案。
这些示例全部 运行 在调用者线程中,但其中 none 将在 get()
方法中执行任务。原则上,这不会违反合同,因为它 returns 结果值,当计算已执行时,但是,在尝试正确实现 get(long timeout, TimeUnit unit)
时可能会遇到麻烦。相比之下,有一个不工作的 cancel()
仍然在合同范围内。