Java 8来, it's time to learn something new. Java 7 and Java 6 are just slightly modified versions, while Java 8 will be significantly improved. Maybe Java 8 is too big, right? Today I'll give you a thorough explanation of the new abstract –completablefuture in JDK 8. As we all know, Java 8 is released in less than a year, so this article is based on the JDK 8 build and lambda support. Completablefuture extends future provides methods, unary operators and the promotion of asynchronous and event-driven programming models that do not stop in the old version of Java. You must be shocked if you open Javadoc of completablefuture. There are about 50 ways (! , and some of them are very interesting and difficult to understand, such as:
Copy Code code as follows:
Public <U,V> completablefuture<v> Thencombineasync (
completablefuture<? Extends u> other,
bifunction<? Super T, huh? Super U, huh? Extends v> FN,
Executor Executor)
Don't worry, keep reading. Completablefuture collects the characteristics of all listenablefuture in guava and settablefuture. In addition, the built-in lambda expression makes it closer to the Scala/akka futures. It sounds incredibly good, but please read on. Completablefuture has two main aspects superior to the future– asynchronous callback/conversion in OL, which enables any thread from any moment to set the Completablefuture value.
First, the value of the extraction and modification of packaging
Typically, futures represents code that runs on other threads, but that's not always the case. Sometimes you want to create a future to show you know what will happen, such as the JMS message arrival. So you have future but there is no potential asynchronous work in the future. You just want to do it in the future when the JMS message arrives, which is driven by an event. In this case, you can simply create a completablefuture to return to your client, as long as you think your results are available, only through complete () can unlock all waiting for future client.
First you can simply create a new completablefuture and give your client:
Copy Code code as follows:
Public completablefuture<string> Ask () {
Final completablefuture<string> future = new completablefuture<> ();
//...
return future;
}
Note that there is no connection between this future and callable, no thread pool or asynchronous work. If the client code now calls ask (), it will block forever. If the registers complete the callback, they will never take effect. So what's the point? Now you can say:
Copy Code code as follows:
... At this point all client Future.get () will get the result of the string, which will take effect immediately after the callback is completed. It is very handy when you want to represent the task of future, and there is no need to compute some thread-executing tasks. Completablefuture.complete () can only be invoked once, and subsequent calls are ignored. But there is also a backdoor called completablefuture.obtrudevalue (...) Please use caution before overwriting the value of a new future.
Sometimes you want to see a signal malfunction, as you know the future object can handle the result or exception it contains. If you want to further pass some exceptions, you can overwrite the previous exception with a completablefuture.completeexceptionally (ex) (or a more powerful method like Obtrudeexception (ex)). Completeexceptionally () can also unlock all waiting clients, but this time throws an exception from Get (). When it comes to get (), there are completablefuture.join () methods that have subtle changes in error handling. But on the whole, they are all the same. Finally, there are Completablefuture.getnow (Valueifabsent) methods that are not blocked but if the future is not finished it will return the default value, which makes it useful to build a robust system that we don't want to wait too long.
The last static method is to use Completedfuture (value) to return objects that have completed future, which can be useful when testing or writing some adapter layers.
Ii. Creating and acquiring Completablefuture
Well, is it our only option to create completablefuture manually? Not necessarily. As with the general futures, we can correlate existing tasks while completablefuture using factory methods:
Copy Code code as follows:
Static <U> completablefuture<u> Supplyasync (supplier<u> Supplier);
Static <U> completablefuture<u> Supplyasync (supplier<u> Supplier, Executor Executor);
Static completablefuture<void> Runasync (Runnable Runnable);
Static completablefuture<void> Runasync (Runnable Runnable, Executor Executor);
Non-parametric method executor is ... The end of the async will also use Forkjoinpool.commonpool () (Global, Universal Pool described in JDK8), which applies to most of the methods in the Completablefuture class. Runasync () is easy to understand, note that it needs runnable, so it returns completablefuture<void> as Runnable does not return any values. If you need to handle asynchronous operations and return results, use SUPPLIER<U>:
Copy Code code as follows:
Final completablefuture<string> future = Completablefuture.supplyasync (new supplier<string> () {
@Override
Public String get () {
... long running ...
return "42";
}
}, executor);
But don't forget, there are lambdas expressions in Java 8!
Copy Code code as follows:
finalcompletablefuture<string> future = Completablefuture.supplyasync (()-> {
... long running ...
return "42";
}, executor);
Or:
Copy Code code as follows:
Final completablefuture<string> future =
Completablefuture.supplyasync (()-> longrunningtask (params), executor);
Although this article is not about lambda, I use lambda expressions quite frequently.
Iii. conversion and role in Completablefuture (thenapply)
I told you Completablefuture was better than future, but you don't know why? Simply put, because Completablefuture is an atom and a factor. Does that not help me? Both Scala and JavaScript allow future to register an asynchronous callback when it is complete, until it is ready for us to wait and stop it. We can simply say that there is a result when we run this function. In addition, we can stack these functions, combine multiple future together and so on. For example, if we convert from string to Integer, we can move from Completablefuture to Completablefuture<integer without association. This is through the Thenapply () method:
Copy Code code as follows:
<U> completablefuture<u> thenapply (function< Super T,? extends u> fn);
<U> completablefuture<u> Thenapplyasync (function< Super T,? extends u> fn);
<U> completablefuture<u> Thenapplyasync (function< Super T,? Extends u> FN, Executor Executor); <p& Gt;</p>
<p> as mentioned above ... The async version provides most of the operations for Completablefuture, so I'll skip them in the later sections. Remember that the first method will call the method in the same thread that the future completes, and the remaining two will call it asynchronously in a different thread pool.
Let's look at the workflow of Thenapply ():</p>
<p><pre class= "Brush:java; Gutter:true; First-line:1; Highlight: []; Html-script:false ">
Completablefuture<string> f1 =//...
completablefuture<integer> F2 = f1.thenapply (Integer::p arseint);
Completablefuture<double> F3 = f2.thenapply (R-> R * R * Math.PI);
</p>
or in a declaration:
Copy Code code as follows:
Completablefuture<double> F3 =
F1.thenapply (Integer::p arseint). Thenapply (R-> R * R * Math.PI);
Here, you will see a sequence of transformations, from string to Integer to double. But most importantly, these transformations do not execute immediately or stop. These transformations do not execute immediately or stop. They just remember that when the original F1 completed the program they were executing. If some conversions are time-consuming, you can provide your own executor to run them asynchronously. Note that this operation is equivalent to a unary map in Scala.
Iv. running the completed code (THENACCEPT/THENRUN)
Copy Code code as follows:
Completablefuture<void> thenaccept (consumer< super t> block);
Completablefuture<void> Thenrun (Runnable action);
There are two typical "final" stage approaches in the future pipeline. They are prepared when you use the value of future, and when Thenaccept () provides the final value, Thenrun executes Runnable, which does not even have the means to compute the value. For example:
Copy Code code as follows:
Future.thenacceptasync (dbl-> log.debug ("Result: {}", Dbl), executor);
Log.debug ("continuing");
... Async variables can also be used in two ways, implicit and explicit actuators, and I don't emphasize this approach too much.
The Thenaccept ()/thenrun () method does not block (even if there is no explicit executor). They are like an event listener/handler that you connect to a future when it will execute for some time. The "continuing" message will appear immediately, although the future is not even completed.
Five, single completablefuture error handling
So far, we have only discussed the results of calculations. What's so unusual about that? Can we handle them asynchronously? Of course!
Copy Code code as follows:
completablefuture<string> safe =
Future.exceptionally (ex-> "We have a problem:" + ex.getmessage ());
When exceptionally () accepts a function, the original future is invoked to throw an exception. We will have the opportunity to convert this exception to some values compatible with the future type to recover. Safe further conversions will no longer produce an exception but instead return a string value from the function that provides the function.
A more flexible approach is to handle () accept a function that receives the correct result or exception:
Copy Code code as follows:
completablefuture<integer> safe = Future.handle (OK, ex)-> {
if (ok!= null) {
return Integer.parseint (OK);
} else {
Log.warn ("Problem", ex);
return-1;
}
});
Handle () is always called, the result and exception are not empty, this is a one-stop strategy.
Six or one combined with two completablefuture
The completablefuture of one of the asynchronous processes is very good but when multiple such futures are grouped together in a variety of ways, it does show its strength.
Vii. combination (link) of these two Futures (Thencompose ())
Sometimes you want to run some future values (when it's ready), but this function also returns future. Completablefuture is flexible enough to understand that our function results should now be the top future, contrasting completablefuture<completablefuture>. Method Thencompose () is equivalent to Scala's Flatmap:
Copy Code code as follows:
<U> completablefuture<u> Thencompose (function< super t,completablefuture<u>> FN);
... Async changes are also available, and in the following case, carefully observe the types and differences of thenapply () (map) and Thencompose () (FLATMAP) when applied Calculaterelevance () method returns Completablefuture:
Copy Code code as follows:
Completablefuture<document> docfuture =//...
Completablefuture<completablefuture<double>> f =
Docfuture.thenapply (this::calculaterelevance);
Completablefuture<double> relevancefuture =
Docfuture.thencompose (this::calculaterelevance);
//...
Private completablefuture<double> calculaterelevance (Document doc)//...
Thencompose () is an important way to allow the construction of robust and asynchronous pipelines without the intermediate steps of blocking and waiting.
Eight, two futures conversion values (Thencombine ())
When Thencompose () is used to link a future, it relies on another thencombine, and when they are finished, it combines two separate futures:
Copy Code code as follows:
<U,V> completablefuture<v> thencombine (completablefuture<? extends u> other, bifunction<? Super T ,? Super U, huh? Extends v> fn)
... Async variables are also available, assuming you have two completablefuture, one loading the customer another loading the nearest shop. They are completely independent of each other, but when they do, you want to use their values to compute route. This is an example of deprivation:
Copy Code code as follows:
completablefuture<customer> customerfuture = loadcustomerdetails (123);
completablefuture<shop> shopfuture = Closestshop ();
Completablefuture<route> routefuture =
Customerfuture.thencombine (Shopfuture, (Cust, shop)-> Findroute (Cust, shop));
//...
Private Route Findroute (customer customer, shop shop)//...
Note that in Java 8 you can use (cust, shop)-> Findroute (Cust, shop) to simply replace the reference to the This::findroute method:
Copy Code code as follows:
Customerfuture.thencombine (Shopfuture, This::findroute);
As you know, we have customerfuture and shopfuture. Then routefuture wrap them and "Wait" for them to complete. When they are ready, it will run the functions we provide to combine all the results (Findroute ()). This routefuture will be completed when two basic futures are completed and Findroute () is completed.
Nine, waiting for all the completablefutures to complete
If it is not the result of a new completablefuture connection, we just want to be notified when it is done, and we can use the method of the Thenacceptboth ()/runafterboth () series (...). The Async variable is also available). They work the same way as thenaccept () and Thenrun (), but wait for two futures instead of one:
Copy Code code as follows:
<U> completablefuture<void> Thenacceptboth (completablefuture<? extends u> other, biconsumer<? Super T, huh? Super u> block)
Completablefuture<void> Runafterboth (completablefuture<?> Other, Runnable action)
Imagine the above example, which is not generating new completablefuture, you just want to send some events or refresh the GUI immediately. This can be easily achieved: Thenacceptboth ():
Copy Code code as follows:
Customerfuture.thenacceptboth (Shopfuture, Cust, shop)-> {
Final Route Route = Findroute (cust, shop);
Refresh GUI with Route
});
I wish I was wrong, but maybe some people would ask themselves a question: why can't I simply block these two futures? It's like:
Copy Code code as follows:
future<customer> customerfuture = loadcustomerdetails (123);
future<shop> shopfuture = Closestshop ();
Findroute (Customerfuture.get (), Shopfuture.get ());
Well, of course you can do that. But the key point is that completablefuture is allowed to be asynchronous, an event-driven programming model rather than blocking and eagerly awaiting results. So functionally, the above two parts of the code are equivalent, but the latter is not necessary to occupy a thread to execute.
Ten, waiting for the first completablefuture to complete the task
Another interesting thing is that COMPLETABLEFUTUREAPI can wait for the first (and all opposite) future to complete. It's handy when you have the results of two of the same types of tasks, and you just need to be concerned about response time, and no task is preferred. API method (...). Async variables are also available):
Copy Code code as follows:
Completablefuture<void> Accepteither (completablefuture< extends T>, Consumer< Super T> block )
Completablefuture<void> Runaftereither (completablefuture<?> Other, Runnable action)
As an example, you have two systems that can be integrated. One has a smaller average response time but has a high standard deviation, and the other is slower in general, but easier to predict. For the better of both worlds (performance and predictability) you can call two systems at the same time and wait for who completes first. This is usually the first system, but when the schedule slows down, the second system can be completed in an acceptable time:
Copy Code code as follows:
Completablefuture<string> fast = Fetchfast ();
Completablefuture<string> predictable = fetchpredictably ();
Fast.accepteither (predictable, s-> {
SYSTEM.OUT.PRINTLN ("Result:" + s);
});
s represents a string obtained from Fetchfast () or fetchpredictably (). We don't need to know or care.
Xi. transformation of the first system in its entirety
Applytoeither () is the predecessor of Accepteither (). When two futures are nearing completion, the latter simply calls some snippets of code, and Applytoeither () returns a new future. When the two initial futures are complete, the new future will also be completed. The API is somewhat similar to (...) Async variables are also available):
Copy Code code as follows:
<U> completablefuture<u> applytoeither (completablefuture<? extends t> other, function<? Super T,U > fn)
This additional FN function can be completed when the first future is invoked. I'm not sure what the purpose of this specialization method is, after all, a person can simply use: fast.applytoeither (predictable). Thenapply (FN). Because we insist on using this API, but we do not need additional features of the application, I will simply use the function.identity () placeholder:
Copy Code code as follows:
Completablefuture<string> fast = Fetchfast ();
Completablefuture<string> predictable = fetchpredictably ();
Completablefuture<string> Firstdone =
Fast.applytoeither (predictable, function.<string>identity ());
The first completed future can be run by. Note that from the customer's point of view, two futures are actually hidden behind the firstdone. The client simply waits for the future to complete and passes the Applytoeither () to notify the client when the first two tasks are complete.
12, a variety of combination of completablefuture
We now know how to wait for two future to complete (using Thencombine ()) and the first to complete (Applytoeither ()). But can it be extended to any number of futures? Indeed, use the static helper method:
Copy Code code as follows:
Static completablefuture<void< AllOf (COMPLETABLEFUTURE<?<. cfs)
Static completablefuture<object< anyof (COMPLETABLEFUTURE<?<. cfs)
AllOf () When all the potential futures are complete, a futures array is used and a future is returned (waiting for all obstacles). On the other hand anyof () will wait for the fastest potential futures, please take a look at the general type of return futures, isn't that what you expected? We'll look at the problem in the next article.
Summarize
We explored the entire completablefuture API. I'm sure this will be invincible, so in the next article we'll look at another simple web crawler implementation, using the Completablefuture method and the Java 8 lambda expression, and we'll see the completablefuture