This document provides an agenda for a presentation on problems with parallel streams and lambda expressions in Java 8. The presentation discusses how parallel streams can sometimes slow down operations when system resources are constrained compared to sequential streams. It also examines how exceptions thrown in lambda expressions result in complex stack traces that are difficult to debug. The document includes links to code examples demonstrating these issues.
Similar to ITSubbotik - как скрестить ежа с ужом или подводные камни внедрения функционального программирования для вашего enterprise java-проекта (20)
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
ITSubbotik - как скрестить ежа с ужом или подводные камни внедрения функционального программирования для вашего enterprise java-проекта
1. 1CONFIDENTIAL
Senior developer
10+ years experience in IT
• 7+ Java-development experience
• 5+ trainer experience
• 3+ system analysis
Interests:
• Messaging
• Functional programming (Clojure,
Scala)
About author
2. 2CONFIDENTIAL
• Parallel Streams can actually slow you down
• The flip-side of Lambda expressions
• Default Methods are distracting
• Checked Exceptions
• Field Access
• Stream API support
• CriptoPro`blem…
• NIO-2 WatchService
• Generics in ENUM`s
AGENDA
3. 3CONFIDENTIAL
Parallel Streams can actually slow you down
• Java 8 brings the promise of parallelism as one of the most anticipated new features. The
.parallelStream() method implements this on collections and streams. It breaks them into
subproblems which then run on separate threads for processing, these can go to different cores and
then get combined when they’re done. This all happens under the hood using the fork/join
framework.
• Ok, sounds cool, it must speed up operations on large data sets in multi-core environments, right?
Stream API in Enterprise Java - Problems
4. 4CONFIDENTIAL
Parallel Streams can actually slow you down
• Let`s test it:
• To sort an array using multiple cores all you have to do is –
– Arrays.parallelSort(numbers);
• To group a collection into different groups based on a specific criteria (e.g. prime and non-prime
numbers)
– Map<Boolean, List> groupByPrimary = numbers.parallelStream()
.collect(Collectors.groupingBy(s -> Utility.isPrime(s)));
• 3 . To filter out values all you have do is –
– Integer[] prims = numbers.parallelStream().filter(s -> Utility.isPrime(s))
.toArray();
Stream API in Enterprise Java - Problems
5. 5CONFIDENTIAL
Parallel Streams can actually slow you down
• https://github.com/takipi/java-8-parallelism-benchmarks
Stream API in Enterprise Java - Problems
6. 6CONFIDENTIAL
Parallel Streams can actually slow you down
• The results were not surprising:
• Quicksort is now 4.7X times faster.
• Grouping is now 5X times faster.
• Filtering is now 5.5X times faster.
• A happy ending? Unfortunately not.
• So what happens under load?
• So far things have been quite peachy, the reason being that there’s little contention between
threads for CPU cycles. That’s an ideal situation, but unfortunately, one that doesn’t happen a
lot in real life. To simulate a scenario which is more on par with what you’d normally see in a
real-world environment I set up a second test. This test runs the same set of algorithms, but
this time executes them on ten concurrent threads to simulate processing ten concurrent
requests performed by a server when it’s under pressure. Each of those requests will then be
handled either sequentially using a traditional approach, or the new Java 8 APIs.
Stream API in Enterprise Java - Problems
7. 7CONFIDENTIAL
Parallel Streams can actually slow you down
• The results:
• Sorting in now only 20% faster – a 23X decline.
• Filtering is now only 20% faster – a 25X decline.
• Grouping is now 15% slower.
Stream API in Enterprise Java - Problems
8. 8CONFIDENTIAL
Parallel Streams can actually slow you down
• https://github.com/takipi/java-8-parallelism-benchmarks
• http://blog.takipi.com/new-parallelism-apis-in-java-8-behind-the-glitz-and-glamour/
• Diagnosis
• Parallelism with all its benefits also brings in additional types of problems to consider.
• When already acting in a multi-threaded environment, keep this in mind and get yourself
familiar with what’s going on behind the scenes.
Stream API in Enterprise Java - Problems
9. 9CONFIDENTIAL
The flip-side of Lambda expressions - 1
• http://blog.takipi.com/the-dark-side-of-lambda-expressions-in-java-8/
• Let’s say I rise up in the morning and want to iterate over a list of world cup teams and map
their lengths:
• List<Boolean> lengths = new ArrayList<>();
for (String country : Arrays.asList(args)) {
lengths.add(check(country));
}
• Now let’s get functional with a nice lambda:
• Stream lengths = countries.stream().map(countries -> check(country));
• …and what if check method throws an Exception?
Stream API in Enterprise Java - Problems
10. 10CONFIDENTIAL
The flip-side of Lambda expressions - 1
• In that case:
• List<Boolean> lengths = new ArrayList<>();
for (String country : Arrays.asList(args)) {
lengths.add(check(country));
}
• , we`ve got:
• at LmbdaMain.check(LmbdaMain.java:19)
at LmbdaMain.main(LmbdaMain.java:34)
Stream API in Enterprise Java - Problems
11. 11CONFIDENTIAL
The flip-side of Lambda expressions - 1
• In that case:
• Stream lengths = countries.stream().map(countries -> check(country));
• , we`ve got:
• at LmbdaMain.check(LmbdaMain.java:19)
at LmbdaMain.lambda$0(LmbdaMain.java:37)
at LmbdaMain$$Lambda$1/821270929.apply(Unknown Source)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.LongPipeline.reduce(LongPipeline.java:438)
at java.util.stream.LongPipeline.sum(LongPipeline.java:396)
at java.util.stream.ReferencePipeline.count(ReferencePipeline.java:526)
at LmbdaMain.main(LmbdaMain.java:39)
Stream API in Enterprise Java - Problems
12. 12CONFIDENTIAL
The flip-side of Lambda expressions - 1
• In that case:
• ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
String js = "var map = Array.prototype.map n";
• js += "var a = map.call(names, function(name) { return
Java.type("LmbdaMain").check(name) }) n";
js += "print(a)";
engine.eval(js);
Stream API in Enterprise Java - Problems
13. 13CONFIDENTIAL
The flip-side of Lambda expressions - 1
• , we`ve got (1):
• LmbdaMain [Java Application]
• LmbdaMain at localhost:51287
• Thread [main] (Suspended (breakpoint at line 16 in LmbdaMain))
• LmbdaMain.wrap(String) line: 16
• 1525037790.invokeStatic_L_I(Object, Object) line: not available
• 1150538133.invokeSpecial_LL_I(Object, Object, Object) line: not available
• 538592647.invoke_LL_I(MethodHandle, Object[]) line: not available
• 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available
• LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147
• LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625
• LambdaForm.interpretWithArguments(Object...) line: 604
• 2150540.interpret_I(MethodHandle, Object, Object) line: not available
• 538592647.invoke_LL_I(MethodHandle, Object[]) line: not available
• 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available
• LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147
• LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625
• LambdaForm.interpretWithArguments(Object...) line: 604
• 92150540.interpret_I(MethodHandle, Object, Object) line: not available
• 38592647.invoke_LL_I(MethodHandle, Object[]) line: not available
• 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available
• LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147
• LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625
Stream API in Enterprise Java - Problems
14. 14CONFIDENTIAL
The flip-side of Lambda expressions - 1
• , we`ve got (2):
• LambdaForm.interpretWithArguments(Object...) line: 604
• 731260860.interpret_L(MethodHandle, Object, Object) line: not available
• LambdaForm$NamedFunction.invoke_LL_L(MethodHandle, Object[]) line: 1108
• 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available
• LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147
• LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625
• LambdaForm.interpretWithArguments(Object...) line: 604
• 2619171.interpret_L(MethodHandle, Object, Object, Object) line: not available
• 1597655940.invokeSpecial_LLLL_L(Object, Object, Object, Object, Object) line: not available
• LambdaForm$NamedFunction.invoke_LLLL_L(MethodHandle, Object[]) line: 1118
• 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available
• LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147
• LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625
• LambdaForm.interpretWithArguments(Object...) line: 604
• 2619171.interpret_L(MethodHandle, Object, Object, Object) line: not available
• 1353530305.linkToCallSite(Object, Object, Object, Object) line: not available
• Script$^eval_._L3(ScriptFunction, Object, Object) line: 3
• 1596000437.invokeStatic_LLL_L(Object, Object, Object, Object) line: not available
• 1597655940.invokeSpecial_LLLL_L(Object, Object, Object, Object, Object) line: not available
• LambdaForm$NamedFunction.invoke_LLLL_L(MethodHandle, Object[]) line: 1118
• 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available
• LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147
Stream API in Enterprise Java - Problems
15. 15CONFIDENTIAL
The flip-side of Lambda expressions - 1
• , we`ve got (3):
• LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625
• LambdaForm.interpretWithArguments(Object...) line: 604
• 484673893.interpret_L(MethodHandle, Object, Object, Object, Object, Object) line: not available
• LambdaForm$NamedFunction.invoke_LLLLL_L(MethodHandle, Object[]) line: 1123
• 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available
• LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147
• LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625
• LambdaForm.interpretWithArguments(Object...) line: 604
• 282496973.interpret_L(MethodHandle, Object, Object, Object, long, Object) line: not available
• 93508253.invokeSpecial_LLLLJL_L(Object, Object, Object, Object, Object, long, Object) line: not available
• 1850777594.invoke_LLLLJL_L(MethodHandle, Object[]) line: not available
• 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available
• LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147
• LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625
• LambdaForm.interpretWithArguments(Object...) line: 604
• 282496973.interpret_L(MethodHandle, Object, Object, Object, long, Object) line: not available
• 293508253.invokeSpecial_LLLLJL_L(Object, Object, Object, Object, Object, long, Object) line: not available
• 1850777594.invoke_LLLLJL_L(MethodHandle, Object[]) line: not available
• 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available
• LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147
• LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625
• LambdaForm.interpretWithArguments(Object...) line: 604
Stream API in Enterprise Java - Problems
16. 16CONFIDENTIAL
The flip-side of Lambda expressions - 1
• , we`ve got (4):
• 1840903588.interpret_L(MethodHandle, Object, Object, Object, Object, long, Object) line: not available
• 2063763486.reinvoke(Object, Object, Object, Object, Object, long, Object) line: not available
• 850777594.invoke_LLLLJL_L(MethodHandle, Object[]) line: not available
• 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available
• LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147
• LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625
• LambdaForm.interpretWithArguments(Object...) line: 604
• 82496973.interpret_L(MethodHandle, Object, Object, Object, long, Object) line: not available
• 220309324.invokeExact_MT(Object, Object, Object, Object, long, Object, Object) line: not available
• NativeArray$10.forEach(Object, long) line: 1304
• NativeArray$10(IteratorAction).apply() line: 124
• NativeArray.map(Object, Object, Object) line: 1315
• 1596000437.invokeStatic_LLL_L(Object, Object, Object, Object) line: not available
• 504858437.invokeExact_MT(Object, Object, Object, Object, Object) line: not available
• FinalScriptFunctionData(ScriptFunctionData).invoke(ScriptFunction, Object, Object...) line: 522
• ScriptFunctionImpl(ScriptFunction).invoke(Object, Object...) line: 207
• ScriptRuntime.apply(ScriptFunction, Object, Object...) line: 378
• NativeFunction.call(Object, Object...) line: 161
• 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available
• 1740189450.invokeSpecial_LLL_L(Object, Object, Object, Object) line: not available
• LambdaForm$NamedFunction.invoke_LLL_L(MethodHandle, Object[]) line: 1113
• 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available
Stream API in Enterprise Java - Problems
17. 17CONFIDENTIAL
The flip-side of Lambda expressions - 1
• , we`ve got (5):
• LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147
• LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625
• LambdaForm.interpretWithArguments(Object...) line: 604
• 2619171.interpret_L(MethodHandle, Object, Object, Object) line: not available
• LambdaForm$NamedFunction.invoke_LLL_L(MethodHandle, Object[]) line: 1113
• 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available
• LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147
• LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625
• LambdaForm.interpretWithArguments(Object...) line: 604
• 323326911.interpret_L(MethodHandle, Object, Object, Object, Object) line: not available
• LambdaForm$NamedFunction.invoke_LLLL_L(MethodHandle, Object[]) line: 1118
• 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available
• LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147
• LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625
• LambdaForm.interpretWithArguments(Object...) line: 604
• 323326911.interpret_L(MethodHandle, Object, Object, Object, Object) line: not available
• 263793464.invokeSpecial_LLLLL_L(Object, Object, Object, Object, Object, Object) line: not available
• LambdaForm$NamedFunction.invoke_LLLLL_L(MethodHandle, Object[]) line: 1123
• 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available
• LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147
• LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625
• LambdaForm.interpretWithArguments(Object...) line: 604
Stream API in Enterprise Java - Problems
18. 18CONFIDENTIAL
The flip-side of Lambda expressions - 1
• , we`ve got (6):
• 1484673893.interpret_L(MethodHandle, Object, Object, Object, Object, Object) line: not available
• 587003819.invokeSpecial_LLLLLL_L(Object, Object, Object, Object, Object, Object, Object) line: not available
• 811301908.invoke_LLLLLL_L(MethodHandle, Object[]) line: not available
• 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available
• LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147
• LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625
• LambdaForm.interpretWithArguments(Object...) line: 604
• 484673893.interpret_L(MethodHandle, Object, Object, Object, Object, Object) line: not available
• LambdaForm$NamedFunction.invoke_LLLLL_L(MethodHandle, Object[]) line: 1123
• 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available
• LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147
• LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625
• LambdaForm.interpretWithArguments(Object...) line: 604
• 323326911.interpret_L(MethodHandle, Object, Object, Object, Object) line: not available
• 2129144075.linkToCallSite(Object, Object, Object, Object, Object) line: not available
• Script$^eval_.runScript(ScriptFunction, Object) line: 3
• 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available
• 1709804316.invokeExact_MT(Object, Object, Object, Object) line: not available
• FinalScriptFunctionData(ScriptFunctionData).invoke(ScriptFunction, Object, Object...) line: 498
• ScriptFunctionImpl(ScriptFunction).invoke(Object, Object...) line: 207
• ScriptRuntime.apply(ScriptFunction, Object, Object...) line: 378
• NashornScriptEngine.evalImpl(ScriptFunction, ScriptContext, ScriptObject) line: 544
Stream API in Enterprise Java - Problems
20. 20CONFIDENTIAL
The flip-side of Lambda expressions - 1
• Diagnosis
• Just stay aware of this, the traces might be a pain from time to time, but it will not keep us
away from them precious lambdas.
Stream API in Enterprise Java - Problems
21. 21CONFIDENTIAL
The flip-side of Lambda expressions – 2
• Overloading gets even worse:
• static <T> T run(Callable<T> c) throws Exception {
return c.call();
}
static <T> T run(Supplier<T> s) throws Exception {
return s.get();
}
• So, we can`t call one of it this way:
• public static void main(String[] args)
throws Exception {
run(() -> null);
// ^^^^^^^^^^ ambiguous method call!
}
• (so, it`s the «static import» and method-pointer`s problem too!)
Stream API in Enterprise Java - Problems
22. 22CONFIDENTIAL
The flip-side of Lambda expressions – 3
• http://blog.jooq.org/2014/04/04/java-8-friday-the-dark-side-of-java-8/
• Not all keywords are supported on default methods:
• They cannot be made final (so, static final too)
• They cannot be made synchronized
• The default keyword:
// Interfaces are always abstract
public /* abstract */ interface NoTrait {
// Abstract methods have no bodies - the abstract keyword is optional
/* abstract */ void run1();
// Concrete methods have bodies - the default keyword is mandatory
default void run2() {}
}
// Classes can optionally be abstract
public abstract class NoInterface {
// Abstract methods have no bodies - the abstract keyword is mandatory
abstract void run1();
// Concrete methods have bodies - the default keyword mustn't be used
void run2() {}
}
Stream API in Enterprise Java - Problems
23. 23CONFIDENTIAL
Default Methods are distracting
http://zeroturnaround.com/rebellabs/how-your-addiction-to-java-8-default-
methods-may-make-pandas-sad-and-your-teammates-angry/
public interface TimeClient {
// ...
static ZoneId getZoneId (String zoneString) {
try {
return ZoneId.of(zoneString);
} catch (DateTimeException e) {
System.err.println("Invalid time zone: " + zoneString + "; using default time zone instead.");
return ZoneId.systemDefault();
}
}
default ZonedDateTime getZonedDateTime(String zoneString) {
return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
}
}
Diagnosis:
When you hold a hammer everything looks like a nail, keep in mind to stick to their original use case, evolution of
an existing interface when a refactor to introduce a new abstract class doesn’t make sense.
Stream API in Enterprise Java - Problems
24. 24CONFIDENTIAL
Checked Exceptions
• lambda expressions can't declare its throws-clause and lambda body can't throw checked exception
such as IOException
Stream API in Enterprise Java - Problems
25. 25CONFIDENTIAL
Checked Exceptions
• lambda expressions can't declare its throws-clause and lambda body can't throw checked exception
such as IOException
• What if we want to make code like this compile:
• public List<Class> getClasses() throws ClassNotFoundException {
return Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(className -> Class.forName(className))
.collect(Collectors.toList());
}
• This code does not compile, since the Class.forName() method above throws
ClassNotFoundException, which is checked.
Stream API in Enterprise Java - Problems
27. 27CONFIDENTIAL
Field Access
• class Person {
public String name;
public String getName() { return name; }
}
• If we can write this code:
• List<Person> persons;
persons.stream().map(Person::getName).collect(Collectors.toList());
persons.stream().map(p -> p.name).collect(Collectors.toList());
• Why can`t we write something like this?
• persons.stream().map(Person::name).collect(Collectors.toList());
Stream API in Enterprise Java - Problems
28. 28CONFIDENTIAL
Stream API support
• Not present for:
• XPath,
• Serialization (Java, XML, JSON, others),
• Messaging (RebbitMQ, ZeroMQ, Kafka),
• NIO2
• JDBC and JPA
• etc.
Stream API in Enterprise Java - Problems
29. 29CONFIDENTIAL
CriptoPro`blem…
• КриптоПро JCP функционирует в следующем окружении:
• виртуальной машина, удовлетворяющая спецификации Sun Java 2 ™ Virtual Machine;
• требуется Java 2 Runtime Environment версии 1.4.2, 1.5.0 для версии JCP 1.0.46,
Java 6 и выше для 1.0.54 (для этой версии - Java до 1.7.0_25) и 2.x;
Stream API in Enterprise Java - Problems
30. 30CONFIDENTIAL
CriptoPro`blem…
• КриптоПро JCP функционирует в следующем окружении:
• виртуальной машина, удовлетворяющая спецификации Sun Java 2 ™ Virtual Machine;
• требуется Java 2 Runtime Environment версии 1.4.2, 1.5.0 для версии JCP 1.0.46,
Java 6 и выше для 1.0.54 (для этой версии - Java до 1.7.0_25) и 2.x;
Stream API in Enterprise Java - Problems