java.util.concurrent.Future Basics
2017-05-10 11:25
423 查看
Hereby I am starting a series of articles about future concept in programming languages (also known as promises or delays) with a working title: Back to the Future. Futures are very important abstraction, even more these day than ever due to growing demand for asynchronous, event-driven, parallel and scalable systems. In the first article we'll discover most basic
In a single-threaded application when you call a method it returns only when the computations are done (
We will implement
In some use cases you might want to peek on the
Imagine a situation where your user waits for some asynchronous
computation and you'd like to let him know that we are still waiting and
do some computation in the meantime:
The last call to
Cancelling futures is the last aspect we have not covered yet. Imagine
you started some asynchronous job and you can only wait for it given
amount of time. If it's not there after, say, 2 seconds, we give up and
either propagate error or work around it. However if you are a good
citizen, you should somehow tell this future object: I no longer need
you, forget about it. You save processing resources by not running
obsolete tasks. The syntax is simple:
We all love cryptic, boolean parameters, aren't we? Cancelling comes in two flavours. By passing
So we now understand what
is - a place-holder for something, that you will get in the
future. It's like keys to a car that was not yet manufactured. But how
do you actually obtain an instance of
in your application? Two most common sources are thread
pools and asynchronous methods (backed by thread pools for you). Thus
our
method can be rewritten to:
A lot of syntax boilerplate, but the basic idea is simple: wrap long-running computations in
and
them to a thread pool of 10 threads. Submitting returns some implementation of
, most likely somehow linked to your task and thread pool. Obviously your task is not executed immediately. Instead it is placed in a queue which is later (maybe even much later) polled by thread from a pool. Now it should be clear what these two flavours of
mean - you can always cancel task that still resides in that queue. But cancelling already running task is a bit more complex.
Another place where you can meet
is Spring and EJB. For example in Spring framework you can simply annotate your method with
Notice that we simply wrap our result in
. But the method itself does not deal with thread pool or asynchronous processing. Later on Spring will proxy all calls to
and run them in a thread pool. The exact same feature is available through
So we learned a lot about
. Now it's time to admit - this interface is quite limited, especially when compared to other languages. More on that later.
java.util.concurrent.Future<T>interface. Later on we will jump into other frameworks, libraries or even languages.
Future<T>is pretty limited, but essential to understand, ekhm, future parts.
In a single-threaded application when you call a method it returns only when the computations are done (
IOUtils.toString()comes from Apache Commons IO):
public String downloadContents(URL url) throws IOException {
try(InputStream input = url.openStream()) {
return IOUtils.toString(input, StandardCharsets.UTF_8);
}
}
//...
final String contents = downloadContents(new URL("http://www.example.com"));
downloadContents()looks harmless1, but it can take even arbitrary long time to complete. Moreover in order to reduce latency you might want to do other, independent processing in the meantime, while waiting for results. In the old days you would start a new
Threadand somehow wait for results (shared memory, locks, dreadful
wait()/
notify()pair, etc.) With
Future<T>it's much more pleasant:
public static Future<String> startDownloading(URL url) {
//...
}
final Future<String> contentsFuture = startDownloading(new URL("http://www.example.com"));
//other computation
final String contents = contentsFuture.get();
We will implement
startDownloading()soon. For now it's important that you understand the principles.
startDownloading()does not block, waiting for external website. Instead it returns immediately, returning a lightweight
Future<String>object. This object is a promise that
Stringwill be available in the future. Don't know when, but keep this reference and once it's there, you'll be able to retrieve it using
Future.get(). In other words
Futureis a proxy or a wrapper around an object that is not yet there. Once the asynchronous computation is done, you can extract it. So what API does
Futureprovide?
Future.get()is the most important method. It blocks and waits until promised result is available (resolved). So if we really need that
String, just call
get()and wait. There is an overloaded version that accepts timeout so you won't wait forever if something goes wild.
TimeoutExceptionis thrown if waiting for too long.
In some use cases you might want to peek on the
Futureand continue if result is not yet available. This is possible with
isDone().
Imagine a situation where your user waits for some asynchronous
computation and you'd like to let him know that we are still waiting and
do some computation in the meantime:
final Future<String> contentsFuture = startDownloading(new URL("http://www.example.com"));
while (!contentsFuture.isDone()) {
askUserToWait();
doSomeComputationInTheMeantime();
}
contentsFuture.get();
The last call to
contentsFuture.get()is guaranteed to return immediately and not block because
Future.isDone()returned
true. If you follow the pattern above make sure you are not busy waiting, calling
isDone()millions of time per second.
Cancelling futures is the last aspect we have not covered yet. Imagine
you started some asynchronous job and you can only wait for it given
amount of time. If it's not there after, say, 2 seconds, we give up and
either propagate error or work around it. However if you are a good
citizen, you should somehow tell this future object: I no longer need
you, forget about it. You save processing resources by not running
obsolete tasks. The syntax is simple:
contentsFuture.cancel(true); //meh...
We all love cryptic, boolean parameters, aren't we? Cancelling comes in two flavours. By passing
falseto
mayInterruptIfRunningparameter we only cancel tasks that didn't yet started, when the
Futurerepresents results of computation that did not even began. But if our
Callable.call()is already in the middle, we let it finish. However if we pass
true,
Future.cancel()will be more aggressive, trying to interrupt already running jobs as well. How? Think about all these methods that throw infamous
InterruptedException, namely
Thread.sleep(),
Object.wait(),
Condition.await(), and many others (including
Future.get()). If you are blocking on any of such methods and someone decided to cancel your
Callable, they will actually throw
InterruptedException, signalling that someone is trying to interrupt currently running task.
So we now understand what
Future<T>
is - a place-holder for something, that you will get in the
future. It's like keys to a car that was not yet manufactured. But how
do you actually obtain an instance of
Future<T>
in your application? Two most common sources are thread
pools and asynchronous methods (backed by thread pools for you). Thus
our
startDownloading()
method can be rewritten to:
private final ExecutorService pool = Executors.newFixedThreadPool(10);
public Future<String> startDownloading(final URL url) throws IOException {
return pool.submit(new Callable<String>() {
@Override
public String call() throws Exception {
try (InputStream input = url.openStream()) {
return IOUtils.toString(input, StandardCharsets.UTF_8);
}
}
});
}
A lot of syntax boilerplate, but the basic idea is simple: wrap long-running computations in
Callable<String>
and
submit()
them to a thread pool of 10 threads. Submitting returns some implementation of
Future<String>
, most likely somehow linked to your task and thread pool. Obviously your task is not executed immediately. Instead it is placed in a queue which is later (maybe even much later) polled by thread from a pool. Now it should be clear what these two flavours of
cancel()
mean - you can always cancel task that still resides in that queue. But cancelling already running task is a bit more complex.
Another place where you can meet
Future
is Spring and EJB. For example in Spring framework you can simply annotate your method with
@Async:
@Async
public Future<String> startDownloading(final URL url) throws IOException {
try (InputStream input = url.openStream()) {
return new AsyncResult<>(
IOUtils.toString(input, StandardCharsets.UTF_8)
);
}
}
Notice that we simply wrap our result in
AsyncResultimplementing
Future
. But the method itself does not deal with thread pool or asynchronous processing. Later on Spring will proxy all calls to
startDownloading()
and run them in a thread pool. The exact same feature is available through
@Asynchronousannotation in EJB.
So we learned a lot about
java.util.concurrent.Future
. Now it's time to admit - this interface is quite limited, especially when compared to other languages. More on that later.
相关文章推荐
- at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:352)
- java.util.concurrent.Future 类基础
- java多线程学习-java.util.concurrent详解(二)Semaphore/FutureTask/Exchanger
- java.util.concurrent中的Callable,Future
- java.util.concurrent.Future 类基础
- Parameter 'xxxx' not found. Available parameters are [list] at java.util.concurrent.FutureTask
- java.util.concurrent.TimeoutException at java.util.concurrent.FutureTask$Sync.innerGet
- java多线程学习-java.util.concurrent详解(二)Semaphore/FutureTask/Exchanger
- java.util.concurrent.RunnableFuture
- 简析Java中的util.concurrent.Future接口
- java.util.concurrent包(4)——Callable和Future
- 多线程之Future和Callable【高性能应用场景java.util.concurrent】
- java多线程学习-java.util.concurrent详解(二)Semaphore/FutureTask/Exchanger
- java.util.concurrent.FutureTask
- java.util.concurrent.Future 类基础
- [转载] java多线程学习-java.util.concurrent详解(二)Semaphore/FutureTask/Exchanger
- JDK源码(FutureTask)——java.util.concurrent(十)
- java.util.concurrent.Callable, Runnable, Future,ExecutorService介绍
- tomcat启动异常--at java.util.concurrent.FutureTask.report(Unknown Source)
- 同步工具类二:可取消异步计算(java.util.concurrent.FutureTask)