@@ -166,6 +166,12 @@ Parallel Streams
166166 - [ Parallel Stream computation takes more than 100us in total?] ( #justify-parallel-stream-use )
167167 - [ Comment before a parallel Streams pipeline explains how it takes more than 100us in total?
168168 ] ( #justify-parallel-stream-use )
169+
170+ Futures
171+ - [ Non-blocking computation needs to be decorated as a ` Future ` ?] ( #unneeded-future )
172+ - [ Method returning a ` Future ` doesn't block?] ( #future-method-no-blocking )
173+ - [ In a method returning a ` Future ` , considered wrapping an "expected" exception within the ` Future ` ?
174+ ] ( #future-method-failure-paths )
169175
170176Thread interruption and ` Future ` cancellation
171177 - [ Interruption status is restored before wrapping ` InterruptedException ` with another exception?
@@ -273,6 +279,10 @@ thread? See [PS.1](#justify-parallel-stream-use) regarding this.
273279See also [ NB.3] ( #non-blocking-warning ) and [ NB.4] ( #justify-busy-wait ) regarding justification of
274280non-blocking code, racy code, and busy waiting.
275281
282+ If the usage of concurrency tools is not justified, there is a possibility of code ending up with
283+ [ unnecessary thread-safety] ( #unneeded-thread-safety ) , [ redundant atomics] ( #redundant-atomics ) ,
284+ [ redundant ` volatile ` modifiers] ( #justify-volatile ) , or [ unneeded Futures] ( #unneeded-future ) .
285+
276286<a name =" threading-flow-model " ></a >
277287[ #] ( #threading-flow-model ) Dc.2. If the patch introduces a new subsystem that uses threads or thread
278288pools, are there ** high-level descriptions of the threading model, the concurrent control flow (or
@@ -536,7 +546,8 @@ be accessed (method called) concurrently from multiple threads (without *happens
536546relationships between the accesses or calls)? Can a class (method) be simplified by making it
537547non-thread-safe?
538548
539- See also [ Dc.9] ( #justify-volatile ) about potentially unneeded ` volatile ` modifiers.
549+ See also [ Ft.1] ( #unneeded-future ) about unneeded wrapping of a computation into a ` Future ` and
550+ [ Dc.9] ( #justify-volatile ) about potentially unneeded ` volatile ` modifiers.
540551
541552This item is a close relative of [ Dn.1] ( #rationalize ) (about rationalizing concurrency and thread
542553safety in the patch description) and [ Dc.1] ( #justify-document ) (about justifying concurrency in
@@ -1296,6 +1307,85 @@ comment, it’s harder to notice the discrepancy and the fact that the computati
12961307fit for parallel Streams. It can be fixed by calling the non-blocking version of the logic again or
12971308by using a simple sequential ` Stream ` instead of a parallel ` Stream ` .
12981309
1310+ ### Futures
1311+
1312+ <a name =" unneeded-future " ></a >
1313+ [ #] ( #unneeded-future ) Ft.1. Does a method returning a ` Future ` do some blocking operation
1314+ asynchronously? If it doesn't, ** was it considered to perform non-blocking computation logic and
1315+ return the result directly from the method, rather than within a ` Future ` ?** There are situations
1316+ when someone might still want to return a ` Future ` wrapping some non-blocking computation,
1317+ essentially relieving the users from writing boilerplate code like
1318+ ` CompletableFuture.supplyAsync(obj::expensiveComputation) ` if all of them want to run the method
1319+ asynchronously. But if at least some of the clients don't need the indirection, it's better not to
1320+ wrap the logic into a ` Future ` prematurely and give the users of the API a choice to do this
1321+ themselves.
1322+
1323+ See also [ ETS.3] ( #unneeded-thread-safety ) about unneeded thread-safety of a method.
1324+
1325+ <a name =" future-method-no-blocking " ></a >
1326+ [ #] ( #future-method-no-blocking ) Ft.2. Aren't there ** blocking operations in a method returning a
1327+ ` Future ` before asynchronous execution is started** , and is it started at all? Here is the
1328+ antipattern:
1329+ ``` java
1330+ Future<Salary > getSalary(Employee employee) throws ConnectionException {
1331+ Branch branch = retrieveBranch(employee); // A database or an RPC call
1332+ return CompletableFuture . supplyAsync(() - > {
1333+ return retrieveSalary(branch, employee); // Another database or an RPC call
1334+ }, someBlockingIoExecutor());
1335+ }
1336+ ```
1337+ Blocking the caller thread is unexpected for a user seeing a method returning a ` Future ` .
1338+
1339+ An example completely without asynchrony:
1340+ ``` java
1341+ Future<Salary > getSalary(Employee employee) throws ConnectionException {
1342+ SalaryDTO salaryDto = retrieveSalary(employee); // A database or an RPC call
1343+ Salary salary = toSalary(salaryDto);
1344+ return completedFuture(salary); // Or Guava's immediateFuture(), Scala's successful()
1345+ }
1346+ ```
1347+
1348+ If the ` retrieveSalary() ` method is not blocking itself, the ` getSalary() ` [ may not need to return
1349+ a ` Future ` ] ( #unneeded-future ) .
1350+
1351+ Another problem with making blocking calls before scheduling an ` Future ` is that the resulting code
1352+ has [ multiple failure paths] ( #future-method-failure-paths ) : either the future may complete
1353+ exceptionally, or the method itself may throw an exception (typically from the blocking operation),
1354+ which is illustrated by ` getSalary() throws ConnectionException ` in the above examples.
1355+
1356+ <a name =" future-method-failure-paths " ></a >
1357+ [ #] ( #future-method-failure-paths ) Ft.3. If a method returns a ` Future ` and some logic in the
1358+ beginning of it may lead to an * expected failure* (i. e. not a result of a programming bug), ** was
1359+ it considered to propagate an expected failure by a ` Future ` completed exceptionally, rather than
1360+ throwing from the method?** For example, the following method:
1361+ ``` java
1362+ Future<Response > makeQuery(String query) throws InvalidQueryException {
1363+ Request req = compile(query); // Can throw an InvalidQueryException
1364+ return CompletableFuture . supplyAsync(() - > service. remoteCall(req), someBlockingIoExecutor());
1365+ }
1366+ ```
1367+ May be converted into:
1368+ ``` java
1369+ Future<Response > makeQuery(String query) {
1370+ try {
1371+ Request req = compile(query);
1372+ } catch (InvalidQueryException e) {
1373+ // Explicit catch preserves the semantics of the original version of makeQuery() most closely.
1374+ // If compile(query) is an expensive computation, it may be undesirable to schedule it to
1375+ // someBlockingIoExecutor() by simply moving compile(query) into the lambda below.
1376+ // Another alternative is
1377+ // supplyAsync(() -> compile(query)).thenApply(service::remoteCall, someBlockingIoExecutor());
1378+ // scheduling compile(query) to the common FJP.
1379+ CompletableFuture<Response > f = new CompletableFuture<> ();
1380+ f. completeExceptionally(e);
1381+ return f; // Or use Guava's immediateFailedFuture()
1382+ }
1383+ return CompletableFuture . supplyAsync(() - > service. remoteCall(req), someBlockingIoExecutor());
1384+ }
1385+ ```
1386+ The point of this refactoring is unification of failure paths, so that the users of the API don't
1387+ have to deal with multiple different ways of handling errors from the method.
1388+
12991389### Thread interruption and ` Future ` cancellation
13001390
13011391<a name =" restore-interruption " ></a >
0 commit comments