|
1 | | -package com.fpinjava.functionaparallelism.exercise03; |
| 1 | +package com.fpinjava.functionalparallelism.exercise03; |
2 | 2 |
|
3 | | -import java.util.concurrent.Callable; |
4 | 3 | import java.util.concurrent.ExecutionException; |
5 | 4 | import java.util.concurrent.ExecutorService; |
6 | | -import java.util.concurrent.Executors; |
7 | 5 | import java.util.concurrent.Future; |
8 | 6 | import java.util.concurrent.TimeUnit; |
9 | 7 | import java.util.concurrent.TimeoutException; |
10 | 8 |
|
11 | 9 | import com.fpinjava.common.Function; |
12 | | -import com.fpinjava.common.List; |
13 | 10 | import com.fpinjava.common.Option; |
14 | 11 | import com.fpinjava.common.Supplier; |
15 | | -import com.fpinjava.common.Tuple; |
16 | 12 |
|
17 | | -/* |
18 | | - * What if `run` were backed by a `java.util.concurrent.ExecutorService`? You |
19 | | - * may want to spend some time looking through the `java.util.concurrent` |
20 | | - * package to see what other useful things you can find. |
21 | | - */ |
22 | 13 | public interface Par<A> extends Function<ExecutorService, Future<A>> { |
23 | 14 |
|
24 | | - public static Par<Integer> sum(List<Integer> ints) { |
25 | | - if (ints.length() <= 1) { |
26 | | - return Par.unit(() -> ints.headOption().getOrElse(0)); |
27 | | - } else { |
28 | | - final Tuple<List<Integer>, List<Integer>> tuple = ints.splitAt(ints.length() / 2); |
29 | | - return Par.map2(fork(() -> sum(tuple._1)), fork(() -> sum(tuple._2)), x -> y -> x + y); |
30 | | - } |
31 | | - } |
32 | | - |
33 | | - /*- |
34 | | - * `map2` doesn't evaluate the call to `f` in a separate logical thread, in |
35 | | - * accord with our design choice of having `fork` be the sole function in the |
36 | | - * API for controlling parallelism. We can always do `fork(map2(a,b)(f))` if |
37 | | - * we want the evaluation of `f` to occur in a separate thread. |
38 | | - * |
39 | | - * This implementation of `map2` does _not_ respect timeouts. It simply passes |
40 | | - * the `ExecutorService` on to both `Par` values, waits for the results of the |
41 | | - * Futures `af` and `bf`, applies `f` to them, and wraps them in a |
42 | | - * `UnitFuture`. In order to respect timeouts, we'd need a new `Future` |
43 | | - * implementation that records the amount of time spent evaluating `af`, then |
44 | | - * subtracts that time from the available time allocated for evaluating `bf`. |
45 | | - */ |
46 | 15 | public static <A, B, C> Par<C> map2(Par<A> a, Par<B> b, Function<A, Function<B, C>> f) { |
47 | 16 | return (ExecutorService es) -> { |
48 | 17 | Future<A> af = a.apply(es); |
49 | 18 | Future<B> bf = b.apply(es); |
50 | 19 | return new Map2Future<>(af, bf, f); |
51 | 20 | }; |
52 | 21 | } |
53 | | - |
| 22 | + |
54 | 23 | public static class Map2Future<A, B, C> implements Future<C> { |
55 | | - |
| 24 | + |
56 | 25 | private volatile Option<C> cache = Option.none(); |
57 | 26 |
|
58 | 27 | private final Future<A> a; |
59 | 28 | private final Future<B> b; |
60 | 29 | private final Function<A, Function<B, C>> f; |
61 | | - |
| 30 | + |
62 | 31 | public Map2Future(Future<A> a, Future<B> b, Function<A, Function<B, C>> f) { |
63 | 32 | super(); |
64 | 33 | this.a = a; |
@@ -91,13 +60,15 @@ public C get() throws InterruptedException, ExecutionException { |
91 | 60 | } |
92 | 61 |
|
93 | 62 | @Override |
94 | | - public C get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { |
| 63 | + public C get(long timeout, TimeUnit unit) throws InterruptedException, |
| 64 | + ExecutionException, TimeoutException { |
95 | 65 | return compute(TimeUnit.MILLISECONDS.convert(timeout, unit)); |
96 | 66 | } |
97 | | - |
98 | | - private C compute(long timeoutMs) throws InterruptedException, ExecutionException, TimeoutException { |
| 67 | + |
| 68 | + private C compute(long timeoutMs) throws InterruptedException, |
| 69 | + ExecutionException, TimeoutException { |
99 | 70 | if (cache.isSome()) { |
100 | | - return cache.get(); |
| 71 | + return cache.get(); |
101 | 72 | } else { |
102 | 73 | final long start = System.currentTimeMillis(); |
103 | 74 | final A ar = a.get(timeoutMs, TimeUnit.MILLISECONDS); |
@@ -132,13 +103,7 @@ public static <A> Par<A> unit(Supplier<A> a) { |
132 | 103 | * later in the chapter. |
133 | 104 | */ |
134 | 105 | public static <A> Par<A> fork(Supplier<Par<A>> a) { |
135 | | - return es -> es.submit(new Callable<A>() { |
136 | | - |
137 | | - @Override |
138 | | - public A call() throws Exception { |
139 | | - return a.get().apply(es).get(); |
140 | | - } |
141 | | - }); |
| 106 | + return es -> es.submit(() -> a.get().apply(es).get()); |
142 | 107 | } |
143 | 108 |
|
144 | 109 | public static <A> Par<A> lazyUnit(Supplier<A> a) { |
|
0 commit comments