Upload
martin-toshev
View
218
Download
7
Embed Size (px)
Citation preview
3 CONFIDENTIAL
Agenda
• CompletableFuture
• ConcurrentHashMap
• Scalable Updatable Variables
• StampedLock
5 CONFIDENTIAL
Parallel Tasks
• Parallel tasks are represented by implementations of the ForkJoinTask that are submitted to a ForkJoinPool instance for execution
• Provides a direct way to implement a divide-and-conquer mechanism for executing multiple tasks in parallel (in regard to executing many independent tasks with the ExecutorService)
6 CONFIDENTIAL
Parallel Tasks
• Typical implementations of parallel tasks do not extend directly ForkJoinTask
• RecursiveTask instances can be used to execute parallel tasks that return a result
• RecursiveAction instances can be used to execute parallel tasks that do not return a result
7 CONFIDENTIAL
Parallel Tasks
• ForkJoinPool.commonPool() - introduced in Java 8 in order to retrieve the common pool that is basically a fork-join pool for all ForkJoinTasks that are not submitted to a pool
• The common pool is used to implement parallel streams and parallel array operations
• The common pool is as easy way to retrieve a ForkJoinPool for parrallel operations that also improves resource utilization
8 CONFIDENTIAL
Parallel Tasks
public class NumberFormatAction extends RecursiveAction {
…
@Override
protected void compute() {
if (end - start <= 2) {
System.out.println(Thread.currentThread().getName() + " " + start + " " +
end);
} else {
int mid = start + (end - start) / 2;
NumberFormatAction left = new NumberFormatAction(start, mid);
NumberFormatAction right = new NumberFormatAction(mid + 1, end);
invokeAll(left, right);
}
public static void main(String[] args) {
NumberFormatAction action = new NumberFormatAction(1, 50);
ForkJoinPool.commonPool().invoke(action);
}}}
10 CONFIDENTIAL
Parallel Streams
• Streams (sequential and parallel) are introduced in java 8 with the utilities in the java.util.stream package
• You can get a parallel stream from a collection using the parallelStream() method or convert a sequential stream to a parallel one using the parallel() method
11 CONFIDENTIAL
Parallel Streams
Stream.of(1,2,3,4,5,6,7,8,9).parallel().forEach(System.out::println);
Stream.of( new Integer[]{1,2,3,4,5,6,7,8,9} ).parallel().count();
LinkedList<Integer> list = new LinkedList<Integer>();
list.add(100); list.add(200); list.add(300);
Stream<Integer> stream1 = list.stream().parallel();
Stream<Integer> stream2 = list.parallelStream();
int[] values = IntStream.range(1000, 2000).parallel().toArray();
Arrays.stream(new double[]{1.1, 2.2, 3.3}).parallel().sum();
12 CONFIDENTIAL
Parallel Streams
• However …
– Be careful when using parallel streams - they may be slower then sequential streams
– Always do proper benchmarks (e.g. using JMH)
13 CONFIDENTIAL
Parallel Streams
• Parallel streams may not be proper when:
– A data structure that cannot be split well is used (e.g. LinkedList)
– Tasks are not independent and they cannot be split independently
– There are a lot of IO operations (file, network etc. ) or synchronization
– Simple operations are performed that may be optimized when using a sequential stream
14 CONFIDENTIAL
Parallel Streams
• Parallel streams may not be proper when:
– A data structure that cannot be split well is used (e.g. LinkedList)
– Tasks are not independent and they cannot be split independently
– There are a lot of IO operations (file, network etc. ) or synchronization
– Simple operations are performed that may be optimized when using a sequential stream
(Some of the limitations are implied due to the shared common Fork/Join pool being used for parallel streams)
15 CONFIDENTIAL
Parallel Streams
• Imagine a JavaEE application creating a parallel stream …
• … and several other applications creating parallel streams
• ALL of them use the common Fork/Join pool by default …
=> Simply do not create a parallel stream in a JavaEE application
16 CONFIDENTIAL
Parallel Streams
public static void listStream() {
List<Integer> numbers = getRandomNumbers(10_000_000); long start = System.nanoTime(); numbers.stream(). filter(num -> num%2 == 0).count(); long end = System.nanoTime(); System.out.println((end – start)/1000);
}
public static void listParallelStream() {
List<Integer> numbers = getRandomNumbers(10_000_000); long start = System.nanoTime(); numbers.parallelStream(). filter(num -> num%2 == 0).count(); long end = System.nanoTime(); System.out.println((end – start)/1000);
}
VS
17 CONFIDENTIAL
Parallel Streams
public static void listStream() {
List<Integer> numbers = getRandomNumbers(10_000_000); long start = System.nanoTime(); numbers.stream(). filter(num -> num%2 == 0).count(); long end = System.nanoTime(); System.out.println((end – start)/1000);
}
public static void listParallelStream() {
List<Integer> numbers = getRandomNumbers(10_000_000); long start = System.nanoTime(); numbers.parallelStream(). filter(num -> num%2 == 0).count(); long end = System.nanoTime(); System.out.println((end – start)/1000);
}
VS
List is a LinkedList instance
64 ms 309 ms
18 CONFIDENTIAL
Parallel Streams
public static void listStream() {
List<Integer> numbers = getRandomNumbers(10_000_000); long start = System.nanoTime(); numbers.stream(). filter(num -> num%2 == 0).count(); long end = System.nanoTime(); System.out.println((end – start)/1000);
}
public static void listParallelStream() {
List<Integer> numbers = getRandomNumbers(10_000_000); long start = System.nanoTime(); numbers.parallelStream(). filter(num -> num%2 == 0).count(); long end = System.nanoTime(); System.out.println((end – start)/1000);
}
VS
List is an ArrayList instance
55 ms 280 ms
19 CONFIDENTIAL
Parallel Streams
public static void listStream() {
List<Integer> numbers = getRandomNumbers(10_000_000); long start = System.nanoTime(); numbers.stream(). filter(num -> num%2 == 0).count(); long end = System.nanoTime(); System.out.println((end – start)/1000);
}
public static void listParallelStream() {
List<Integer> numbers = getRandomNumbers(10_000_000); long start = System.nanoTime(); numbers.parallelStream(). filter(num -> num%2 == 0).count(); long end = System.nanoTime(); System.out.println((end – start)/1000);
}
VS
(second run for both methods)
6 ms 1 ms
20 CONFIDENTIAL
Parallel Streams
• System.nanoTime() is the one of the most naïve (and incorrect) approaches for doing benchmarks in practices …
• It does not take into account:
– a warm up phase before timing (triggers all initializations and JIT compilations)
– side work done by the JVM (such as GC, output from another threads …)
– many other things depending on the type of performance statistics being gathered …
21 CONFIDENTIAL
Parallel Streams
• Possible libraries you can use to measure performance of parallel streams include:
– JMH
– Caliper
– JUnitBenchmarks
22 CONFIDENTIAL
Parallel Streams
• The limitations imposed by the Fork/Join framework used by parallel streams leads to a rigid critique:
“The JDK1.8 engineers are using the recursively decomposing F/J framework, with its very, very narrow measure, as the engine for parallel programming support simply because they don’t have anything else.”
25 CONFIDENTIAL
Parallel Array Operations
• JDK 8 introduces a number of new methods in the Arrays utility that allow parallel manipulation of arrays
• The methods are divided in three categories:
– parallelSort() – sorts an array in parallel
– parallelPrefix() – performs a cumulative operations on an array in parallel
– parallelSetAll() – sets all of the elements in an array in parallel
26 CONFIDENTIAL
Parallel Streams
Integer[] array = { 16, 7, 26, 14, 77 };
Arrays.parallelSort(array); [7, 14, 16, 26, 77]
Integer[] array = { 16, 7, 26, 14, 77 };
Arrays.parallelPrefix(array, (a, b) -> a * b); [16, 112, 2912, 40768, 3139136]
Integer[] array = new Integer[7];
Arrays.parallelSetAll(array, i -> i); [0, 1, 2, 3, 4, 5, 6]
28 CONFIDENTIAL
CompletableFuture
• Provides a facility to create a chain of dependent non-blocking tasks - an asynchronous task can be triggered as the result of a completion of another task
• A CompletableFuture may be completed/cancelled by a thread prematurely
• Such a facility is already provided by Google's Guava library
Task 1 Task 2 Task 3 Task 4 triggers triggers triggers
29 CONFIDENTIAL
CompletableFuture
• Provides a very flexible API that allows additionally to:
o combine the result of multiple tasks in a CompletableFuture
o provide synchronous/asynchronous callbacks upon completion of a task
o provide a CompletableFuture that executes when first task in group completes
o provide a CompletableFuture that executes when all tasks in a group complete
30 CONFIDENTIAL
CompletableFuture
CompletableFuture<Integer> task1 = new
CompletableFuture<Integer>();
// forcing completing of future by specifying result
task1.complete(10);
31 CONFIDENTIAL
CompletableFuture
CompletableFuture<Integer> task1 = CompletableFuture
.supplyAsync(() -> { … return 10;});
// executed on completion of the future
task1.thenApply((x) -> {…});
// executed in case of exception or completion of the
future
task1.handle((x, y) -> {…});
// can be completed prematurely with a result
// task1.complete(20);
System.err.println(task1.get());
32 CONFIDENTIAL
CompletableFuture
CompletableFuture<Object> prev = null;
Supplier<Object> supplier = () -> { … };
for (int i = 0; i < count; i++) {
CompletableFuture<Object> task;
if (prev != null) {
task = prev.thenCompose((x) -> {
return CompletableFuture.supplyAsync(supplier);});
} else {
task = CompletableFuture.supplyAsync(supplier);
}
prev = task;
}
prev.get();
34 CONFIDENTIAL
ConcurrentHashMap
• ConcurrentHashMap<V, K> class completely rewritten in order to improve its usage as a cache and several new methods have been added as part of the new stream and lambda expressions: o forEach
o forEachKey
o forEachValue
o forEachEntry
36 CONFIDENTIAL
Scalable Updatable Variables
• Maintaining a single variable that is updatable from many threads is a common scalability issue
• Atomic variables already present in the JDK serve as a means to implement updatable variables in a multithreaded environment
• New classes are introduced in order to reduce atomicity guarantees in favor of high throughput - DoubleAccumulator, DoubleAdder, LongAccumulator, LongAdder
37 CONFIDENTIAL
DoubleAccumulator accumulator = new DoubleAccumulator((x, y) -> x + y, 0.0);
// code being executed from some threads
accumulator.accumulate(10);
accumulator.accumulate(20);
System.out.println(accumulator.doubleValue());
Scalable Updatable Variables
39 CONFIDENTIAL
StampedLock
• A very specialized type of explicit lock
• Similar to ReentrantReadWriteLock but provides additionally conversion between the lock modes (writing, reading and optimistic reading)
• Optimistic read lock is a "cheap" version of a read lock that can be invalidated by a read lock
• Lock state is determined by version and lock mode
40 CONFIDENTIAL
StampedLock
• Example
StampedLock sl = new StampedLock();
long stamp = sl.writeLock();
try {
// do something that needs exclusive locking
} finally {
sl.unlockWrite(stamp);
}
41 CONFIDENTIAL
StampedLock
• Example
public long getValue() {
long stamp = sl.tryOptimisticRead();
long value = this.value;
if (!sl.validate(stamp)) {
stamp = sl.readLock();
try {
value = this.value;
} finally {
sl.unlockRead(stamp);
}
}
return value;
}
43 CONFIDENTIAL
Resources
Concurrency Utility Enhancements in Java 8
https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/changes8.html
JVM concurrency: Java 8 concurrency basics
http://www.ibm.com/developerworks/library/j-jvmc2/index.html
Everything about Java 8
http://www.techempower.com/blog/2013/03/26/everything-about-java-8/
Java Specialists newsletter
http://www.javaspecialists.eu/archive/archive.jsp
44 CONFIDENTIAL
Resources
Java Tutorials: Parallelism
https://docs.oracle.com/javase/tutorial/collections/streams/parallelism.html
Think twice before using Java 8 parallel streams
http://java.dzone.com/articles/think-twice-using-java-8
Parallel operations in Java 8
http://www.drdobbs.com/jvm/parallel-array-operations-in-java-8/240166287
Package java.util.stream
https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html
How do I write a correct microbenchmark in Java
http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java
45 CONFIDENTIAL
Resources
Fork/Join Framework Tutorial: ForkJoinPool Example
http://howtodoinjava.com/2014/05/27/forkjoin-framework-tutorial-forkjoinpool-example/
Java 8 tutorial: Streams by Examples
http://howtodoinjava.com/2014/04/13/java-8-tutorial-streams-by-examples/
When to use parallel streams
http://gee.cs.oswego.edu/dl/html/StreamParallelGuidance.html
A Java Fork/Join Calamity
http://www.coopsoft.com/ar/CalamityArticle.html
A Java Parallel Calamity
http://www.coopsoft.com/ar/Calamity2Article.html
46 CONFIDENTIAL
Resources
Java Parallel Stream Performance
http://stackoverflow.com/questions/22999188/java-parallel-stream-performance
Java Theory and practice: Anatomy of a flawed microbenchmark
https://www.ibm.com/developerworks/java/library/j-jtp02225/
Java 8 Concurrency Tutorial: Synchronization and Locks
http://winterbe.com/posts/2015/04/30/java8-concurrency-tutorial-synchronized-locks-examples/