concurrency utilities in java 8

46
1 CONFIDENTIAL Concurrency Utilities in Java 8 04.07.2015

Upload: martin-toshev

Post on 07-Aug-2015

218 views

Category:

Documents


7 download

TRANSCRIPT

1 CONFIDENTIAL

Concurrency Utilities in

Java 8

04.07.2015

2 CONFIDENTIAL

Agenda

• Parallel Tasks

• Parallel Streams

• Parallel Array operations

3 CONFIDENTIAL

Agenda

• CompletableFuture

• ConcurrentHashMap

• Scalable Updatable Variables

• StampedLock

4 CONFIDENTIAL

Parallel Tasks

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);

}}}

9 CONFIDENTIAL

Parallel Streams

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.”

23 CONFIDENTIAL

Parallel Streams

(demo)

24 CONFIDENTIAL

Parallel Array Operations

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]

27 CONFIDENTIAL

CompletableFuture

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();

33 CONFIDENTIAL

ConcurrentHashMap

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

35 CONFIDENTIAL

Scalable Updatable Variables

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

38 CONFIDENTIAL

StampedLock

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;

}

42 CONFIDENTIAL

Thank you

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/