3. Java 8 Basics
Lambda
3
โช A representation of an anonymous function which is passed
around.
โช It doesnโt have a name, but has a list of parameters, a body, a
return type.
4. Java 8 Basics
Lambda
4
Before:
Comparator<Apple> byWeight = new Comparator<Apple>() {
public int compare(Apple a1, Apple a2) {
return a1.getWeight().compareTo(a2.getWeight());
}
};
After:
Comparator<Apple> byWeight =
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
7. Java 8 Basics
Streams
7
โช Source - A data source such as collections, arrays, or I/O resources, where streams
consumes the elements.
โช Sequence of elements - A stream provides a way to a get sequenced set of values of
a specific element type from a source. Collections are about data but streams are
about computations that applied on it.
โช Data processing operations - Supports common set of operations like functional
programming languages to manipulate data, such as filter, map, reduce, find, sort,
etc. Operations can be executed either sequentially or in parallel.
8. Java 8 Basics
8
public static List<String> getLowCaloricDishesNamesInJava7(List<Dish> dishes) {
List<Dish> lowCaloricDishes = new ArrayList<>();
for (Dish d : dishes) {
if (d.getCalories() < 400) {
lowCaloricDishes.add(d);
}
}
List<String> lowCaloricDishesName = new ArrayList<>();
Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
public int compare(Dish d1, Dish d2) {
return Integer.compare(d1.getCalories(), d2.getCalories());
}
});
for (Dish d : lowCaloricDishes) {
lowCaloricDishesName.add(d.getName());
}
return lowCaloricDishesName;
}
12. Streams vs Parallel Streams
โช Parallel stream splits its data elements into multiple chunks, processing
each chunk with a different thread.
โช Automatically partition the workload of a given operation on all the
cores of your multicore processor and keep all of them busy that
idling.
13. Streams vs Parallel Streams
โช Sequential version
public static long sequentialSum(long n) {
return Stream.iterate(1L, i -> i + 1)
.limit(n)
.reduce(Long::sum)
.get();
}
14. Streams vs Parallel Streams
โช Parallel version
public static long parallelSum(long n) {
return Stream.iterate(1L, i -> i + 1)
.limit(n)
.parallel()
.reduce(Long::sum)
.get();
}
15. Reference : Java 8 in Action - Lambdas, streams, and functional-style programming
17. Parallel Streams - Best Practices
โช Automatic boxing and unboxing operations can dramatically hurt performance
โช Some operations naturally perform worse on a parallel stream than on a
sequential stream
- findAny will perform better than findFirst - as no ordering is needed
โช For small amount of data, using a parallel stream is not the correct choice
(almost)
โช Parallel stream isnโt always faster than the corresponding sequential version and
can sometimes work in a counterintuitive way
- check their performance with an appropriate benchmark.
18. Executor Framework
โช Introduced in Java 5 (ExecutorService) as a higher level replacement for
working with threads directly.
โช Capable of running asynchronous tasks and allow to manage a pool of
threads rather than working with them directly.
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
System.out.println("Hello " + threadName);
});
19. Executor Framework
Using Callable
ExecutorService executor = Executors.newFixedThreadPool(1);
Future<Integer> future = executor.submit(() -> {
try {
TimeUnit.SECONDS.sleep(1);
return 777;
} catch (InterruptedException e) {
throw new IllegalStateException("Future was interrupted", e);
}
});
System.out.println("Is the future done ? : " + future.isDone());
Integer result = future.get();
System.out.println("Is the future done? : " + future.isDone());
System.out.println("Result : " + result);
24. Fork/Join Pool
โช Introduced in Java 7 (Java 8 features use it internally)
โช Recursively split a parallelizable task into smaller tasks and then combine
the results of each subtask to produce the final result.
โช pseudocode:
if (task is no longer divisible or small enough) {
sequentially compute the task
} else {
split task in two sub-tasks
recursively call this method to further split each task
wait for the completion of all subtasks
finally combine the results of each subtask
}
25. Reference : Java 8 in Action - Lambdas, streams, and functional-style programming
27. CompletableFuture
Future
โช Models an asynchronous computation and provides a reference to its
result that will be available when the computation is completed.
โช Invoking a potentially time-consuming operation inside a Future allows
the caller thread to return immediately and continue doing useful work
instead of getting blocked until the operation finishes.
29. Limitations with Future
โช Combining two asynchronous computations in one, when theyโre
independent and when the second depends on the result of the first
โช Waiting for the completion of all tasks performed by a set of Futures
โช Waiting for the completion of only the quickest task in a set of Futures
โช Reacting to a Future completion (i.e, being notified when the
completion happens and then having the ability to perform a further
action using the result of the Future, instead of being blocked)
30. Requirement for CompletableFuture
โช The fork/join framework and parallel streams are valuable tools for
parallelism; they split an operation into multiple sub-operations and
perform those sub-operations in parallel on different cores, CPUs.
โช But donโt want to block the computations and waste precious clock
cycles of your CPU.
31. CompletableFuture
To demonstrate the CompletableFuture features, will be using a
best-price-finder application that contacts multiple online shops to find the
lowest price for a given product or service. This example is used in the
reference book - Java 8 in Action.
Following high level steps will be used to explain CompletableFuture
โช How to provide an asynchronous API for your consumers
โช How to make your code non-blocking when youโre a consumer of a
synchronous (blocking) API
โช How to reactively process events representing the completion of an
asynchronous operation
32. CompletableFuture
sequentially querying all the shops
public List<String> findPricesSequential(String product) {
return shops.stream()
.map(shop -> shop.getPrice(product))
.collect(Collectors.toList());
}
33. CompletableFuture
parallely querying all the shops
public List<String> findPricesParallel(String product) {
return shops.parallelStream()
.map(shop -> shop.getPrice(product))
.collect(Collectors.toList());
}
34. CompletableFuture
List<CompletableFuture<String>> priceFutures = shops.stream()
.map(shop -> CompletableFuture.supplyAsync( () ->
String.format("%s price is %.2f", shop.getName(), shop.getPrice(product))))
.collect(toList());
priceFutures.stream()
.map(CompletableFuture::join)
.collect(toList());
supplyAsync : accepts a Supplier as argument and returns a CompletableFuture that will be
asynchronously completed with the value obtained by invoking that Supplier.
36. CompletableFuture
Pipelining asynchronous tasks
Adding another asynchronous task to the pipeline :
โช Suppose that all the shops have agreed to use a centralized discount service.
โช The discount service has an applyDiscount method returning the discounted
price for the product quote given.
37. CompletableFuture
Using the discount service
public List<String> findPricesSequential(String product) {
return shops.stream()
.map(shop -> shop.getPrice(product))
.map(Quote::parse)
.map(Discount::applyDiscount)
.collect(toList());
}
39. Reference : Java 8 in Action - Lambdas, streams, and functional-style programming
40. CompletableFuture
Composing synchronous and asynchronous operations
โช supplyAsync (Getting the prices) - query the shop asynchronously. The
result of this first transformation is a CompletableFuture which contains the
String returned by the corresponding shop.
โช thenApply (Parsing the quotes) - convert those Strings into Quotes with a
second transformation. But because this parsing operation isnโt invoking any
remote service (no I/O in general), it can be invoked synchronously.
41. CompletableFuture
Composing synchronous and asynchronous operations
โช thenCompose (Composing two different futures) - involves contacting the
remote Discount service to apply the appropriate discount percentage to the
non-discounted prices. This is executed remotely and for this reason we
want to perform it asynchronously - by creating another CompletableFuture
using supplyAsync method.
โช thenCompose allows to pipeline two asynchronous operations, passing the
result of the first operation to the second operation when it becomes
available.
43. CompletableFuture
Reacting to a CompletableFuture completion
โช thenAccept : specifies how to consume the result produced by the
CompletableFuture when it becomes available and returns a
CompletableFuture<Void>.
CompletableFuture[] futures = findPricesStream(product)
.map(f -> f.thenAccept(s -> System.out.println(s + " (done in " +
((System.nanoTime() - start) / 1_000_000) + " msecs)")))
.toArray(CompletableFuture[]::new);
CompletableFuture.allOf(futures).join();
45. CompletableFuture
Using custom executor
โช The parallel stream version performs so well only because it can run 8 tasks
(8 core) in parallel, so itโs able to allocate exactly one thread for each shop.
โช But what happens if you decide to add a 9th shop to the list of shops?
47. ConcurrentHashMap Improvements
โช With Java 8, ConcurrentHashMap has been enhanced with a set of
new methods to perform parallel operations effectively on it.
โช forEach(): is capable of iterating over the key-value pairs of the map in
parallel.
โช search(): returns a non-null search result for the current key-value pair
or null if the current iteration doesn't match the desired search criteria
passed as a BiFunction. (variants searchKeys, searchValues)
โช reduce(): accepts two lambda expressions, first function transforms
each key-value pair into a single value of any type. The second function
combines all those transformed values into a single result - like map
and then reduce
48. ConcurrentHashMap Improvements
โ Other methods
โ putIfAbsent
โ getOrDefault
โ replaceAll
โ compute: with two variants computeIfAbsent() and
computeIfPresent()
โ forEachKey
โ forEachValue
50. References
โ Java 8 in Action - Lambdas, streams, and functional-style programming by
Raoul-Gabriel Urma, Mario Fusco, and Alan Mycroft
โ Examples - https://github.com/java8/Java8InAction
โ http://winterbe.com/posts/2015/04/07/java8-concurrency-tutorial-thread-executor-exam
ples/
โ Examples - https://github.com/winterbe/java8-tutorial
โ Examples used in the session : https://github.com/Kishanthan/java8