2011-11-02 | 10:00 AM - 11:00 AM | Victoria
Oracle has initiated a renewal of Java and one of the more important goals is to make it easier to write concurrent software that makes use of all cores in modern hardware. This will require changes both to the language Java as well as its virtual machine. I will describe the latest design that we are working on and demonstrate what can be achieved today using the JRockit JVM and how we can improve concurrency in javac itself.
Java Core | Concurrency in the Java Language and Platform | Fredrik Ohrstrom
1. Concurrency in the Java Language and Platform
Fredrik Öhrström
Principal Member of Technical Staff
JRockit+Hotspot
. . . . . .
2. Concurrency in the Java Language and Platform
Fredrik Öhrström
Principal Member of Technical Staff
JRockit+Hotspot
I have worked on the JRockit JVM for the last 6 six years and on
the OpenJDK during the last year.
Thus I am bi-jvm-lingual.
I am now working in the language team led
by the Java Language Architect Brian Goetz.
Though I am currently sidetracked to rewrite the build system for
OpenJDK.
. . . . . .
3. The following is intended to outline our general product direction.
It is intended for information purposes only, and may not be
incorporated into any contract. It is not a commitment to deliver
any material, code, or functionality, and should not be relied upon
in making purchasing decisions. The development, release, and
timing of any features or functionality described for Oracle’s
products remains at the sole discretion of Oracle.
. . . . . .
4. In the beginning
In 1974, Donald Knuth wrote a paper with the title
Structured Programming with goto statements.
. . . . . .
5. In the beginning
In 1974, Donald Knuth wrote a paper with the title
Structured Programming with goto statements.
“In the late 1960s we witnessed a ’software crisis,’ which
many people thought paradoxical because programming was
supposed to be easy.”
. . . . . .
6. In the beginning
In 1974, Donald Knuth wrote a paper with the title
Structured Programming with goto statements.
“In the late 1960s we witnessed a ’software crisis,’ which
many people thought paradoxical because programming was
supposed to be easy.”
It then takes him approx 60 pages to discuss the problems
with for-loops that were relevant at the time.
. . . . . .
7. In the beginning
In 1974, Donald Knuth wrote a paper with the title
Structured Programming with goto statements.
“In the late 1960s we witnessed a ’software crisis,’ which
many people thought paradoxical because programming was
supposed to be easy.”
It then takes him approx 60 pages to discuss the problems
with for-loops that were relevant at the time.
Today, it is even more complicated!
. . . . . .
8. double highestScore = 0.0;
for (Student s : students) {
if (s.gradYear == 2010) {
if (s.score > highestScore) {
highestScore = s.score;
}
}
}
. . . . . .
9. double highestScore = 0.0;
for (Student s : students) {
if (s.gradYear == 2010) {
if (s.score > highestScore) {
highestScore = s.score;
}
}
}
Traversal logic is encoded into bytecode.
. . . . . .
10. double highestScore = 0.0;
for (Student s : students) {
if (s.gradYear == 2010) {
if (s.score > highestScore) {
highestScore = s.score;
}
}
}
Traversal logic is encoded into bytecode.
Business logic is stateful.
. . . . . .
11. double highestScore = 0.0;
for (Student s : students) {
if (s.gradYear == 2010) {
if (s.score > highestScore) {
highestScore = s.score;
}
}
}
Traversal logic is encoded into bytecode.
Business logic is stateful.
Existing collections impose external iteration
. . . . . .
12. double highestScore = 0.0;
for (Student s : students) {
if (s.gradYear == 2010) {
if (s.score > highestScore) {
highestScore = s.score;
}
}
}
Traversal logic is encoded into bytecode.
Business logic is stateful.
Existing collections impose external iteration
For complex datasets the iterator becomes unnecessary
complex.
. . . . . .
13. This might sound like a theoretical exercise, but Oracle is affected
by the lack of concurrency in Javac, right at this very moment.
. . . . . .
19. Developers need easy to use parallel libraries
One of Java’s strengths has always been its libraries
Ideally the library should decide which strategy to use to
decompose the problem into parallel parts.
Obvious places are the collection classes, add parallel: filter,
sort, map/reduce, select, collect, detect, reject
. . . . . .
20. Developers need easy to use parallel libraries
One of Java’s strengths has always been its libraries
Ideally the library should decide which strategy to use to
decompose the problem into parallel parts.
Obvious places are the collection classes, add parallel: filter,
sort, map/reduce, select, collect, detect, reject
Why don’t we see more parallel libraries today?
. . . . . .
21. Without more language support for
parallel idioms, people will
instinctively reach for serial idioms.
. . . . . .
30. Code as data?
double highestScore =
students.filter(s -> s.gradYear == 2010)
.map(s -> s.score)
.max();
An -> identifies a lambda expression
. . . . . .
31. Code as data?
double highestScore =
students.filter(s -> s.gradYear == 2010)
.map(s -> s.score)
.max();
An -> identifies a lambda expression
Target type parameters
. . . . . .
32. Code as data?
double highestScore =
students.filter(s -> s.gradYear == 2010)
.map(s -> s.score)
.max();
An -> identifies a lambda expression
Target type parameters
More than one parameter (a,b)->c
. . . . . .
33. Code as data?
double highestScore =
students.filter(s -> s.gradYear == 2010)
.map(s -> s.score)
.max();
An -> identifies a lambda expression
Target type parameters
More than one parameter (a,b)->c
Body may be an expression or {statements}
. . . . . .
34. Code as data?
double highestScore =
students.filter(s -> s.gradYear == 2010)
.map(s -> s.score)
.max();
Code reads like the problem statement: Find the highest score
of the students who graduated in 2010”
. . . . . .
35. Code as data?
double highestScore =
students.filter(s -> s.gradYear == 2010)
.map(s -> s.score)
.max();
Code reads like the problem statement: Find the highest score
of the students who graduated in 2010”
Shorter than nested for loops, and potentially faster because
the collection implementation determines how to iterate
. . . . . .
36. Code as data?
double highestScore =
students.filter(s -> s.gradYear == 2010)
.map(s -> s.score)
.max();
Code reads like the problem statement: Find the highest score
of the students who graduated in 2010”
Shorter than nested for loops, and potentially faster because
the collection implementation determines how to iterate
filter() method body can exploit representation knowledge
. . . . . .
37. Code as data?
double highestScore =
students.filter(s -> s.gradYear == 2010)
.map(s -> s.score)
.max();
Code reads like the problem statement: Find the highest score
of the students who graduated in 2010”
Shorter than nested for loops, and potentially faster because
the collection implementation determines how to iterate
filter() method body can exploit representation knowledge
Opportunities for lazy evaluation in filter() and map()
. . . . . .
38. Code as data?
double highestScore =
students.filter(s -> s.gradYear == 2010)
.map(s -> s.score)
.max();
Code reads like the problem statement: Find the highest score
of the students who graduated in 2010”
Shorter than nested for loops, and potentially faster because
the collection implementation determines how to iterate
filter() method body can exploit representation knowledge
Opportunities for lazy evaluation in filter() and map()
Opportunities for parallelism
. . . . . .
39. Lambda Expressions
The name comes from the lambda calculus created by Church
(1936)
. . . . . .
40. Lambda Expressions
The name comes from the lambda calculus created by Church
(1936)
Later explored by Steele and Sussman (1975-1980)
. . . . . .
41. Lambda Expressions
The name comes from the lambda calculus created by Church
(1936)
Later explored by Steele and Sussman (1975-1980)
Thus by adding lambda expressions we have moved from 1974
(Knuth’s structured gotos) to 1975 (Scheme and lexical
lambdas).
. . . . . .
42. Lambda Expressions
The name comes from the lambda calculus created by Church
(1936)
Later explored by Steele and Sussman (1975-1980)
Thus by adding lambda expressions we have moved from 1974
(Knuth’s structured gotos) to 1975 (Scheme and lexical
lambdas).
Yay!
. . . . . .
43. Lambda Expressions
A lambda expression is a lexically scoped anonymous method.
. . . . . .
44. Lambda Expressions
A lambda expression is a lexically scoped anonymous method.
Lexical scoping: can read variables from the lexical
environment, including “this”, unlike inner classes.
No shadowing of lexical scope, unlike inner classes.
Not a member of any class, unlike inner classes.
. . . . . .
45. Why were lambda expressions not added earlier to Java?
. . . . . .
46. Why were lambda expressions not added earlier to Java?
After all, Steele who wrote the lambda papers in 1975, worked
in the Java team for several years!
. . . . . .
47. Why were lambda expressions not added earlier to Java?
After all, Steele who wrote the lambda papers in 1975, worked
in the Java team for several years!
The reason was the design decision that every memory
allocation should be visible as a “new” in Java source code.
. . . . . .
48. Why were lambda expressions not added earlier to Java?
After all, Steele who wrote the lambda papers in 1975, worked
in the Java team for several years!
The reason was the design decision that every memory
allocation should be visible as a “new” in Java source code.
And the reason for this decision was that the hardware of the
time simply was not powerful enough.
. . . . . .
49. Why were lambda expressions not added earlier to Java?
After all, Steele who wrote the lambda papers in 1975, worked
in the Java team for several years!
The reason was the design decision that every memory
allocation should be visible as a “new” in Java source code.
And the reason for this decision was that the hardware of the
time simply was not powerful enough.
But now we have enough hardware to be able to spend
significant cpu power on the optimization problems and GC to
make this less of a problem.
. . . . . .
51. What is the type of a lambda expression?
s -> s.gradYear == 2010
Morally, a function type from Student to boolean
But Java does not have function types....
. . . . . .
52. What is the type of a lambda expression?
s -> s.gradYear == 2010
How would we write a function type?
How would it interact with boxing?
How would it interact with generics?
How would it describe checked exceptions?
Would it be possible to overload based on function type?
. . . . . .
53. In Java we use interfaces (and abstract classes) in two different
ways:
. . . . . .
54. In Java we use interfaces (and abstract classes) in two different
ways:
To specify behaviour: PrintWriter.println(),
Student.setGrade(), GraphicsObject.render().
. . . . . .
55. In Java we use interfaces (and abstract classes) in two different
ways:
To specify behaviour: PrintWriter.println(),
Student.setGrade(), GraphicsObject.render().
To specify calling parameters and return value:
Runnable.run(), Callable.call(), Comparator.compare(),
ActionListener.actionPerformed(),TimerTask.run()
. . . . . .
56. In Java we use interfaces (and abstract classes) in two different
ways:
To specify behaviour: PrintWriter.println(),
Student.setGrade(), GraphicsObject.render().
To specify calling parameters and return value:
Runnable.run(), Callable.call(), Comparator.compare(),
ActionListener.actionPerformed(),TimerTask.run()
We can reuse the second kind of interfaces as function types. For
backwards compatibility. When we have reified Java, then we can
more easily add real function types.
. . . . . .
57. Introducing SAM-types
A SAM-type is an interface or abstract class with a Single Abstract
Method
interface Runnable { void run(); }
interface Callable<T> { T call(); }
interface Comparator<T> { boolean compare(T x, T y); }
interface ActionListener { void actionPerformed(...);
abstract class TimerTask { ... abstract void run(); ... }
. . . . . .
58. The type of a lambda expression is a SAM-type
“SAM-conversion” infers a SAM-type for a lambda expression:
Predicate<Student> p = s -> s.gradYear == 2010;
Invoking the SAM-type’s method invokes the lambdas’s body
boolean ok = p.isTrue(aStudent);
Instant compatibility with existing libraries!
executor.submit( ()->{ println(“Boo”); });
. . . . . .
59. The type of a lambda expression is a SAM-type
“SAM-conversion” infers a SAM-type for a lambda expression:
Predicate<Student> p = s -> s.gradYear == 2010;
Invoking the SAM-type’s method invokes the lambdas’s body
boolean ok = p.isTrue(aStudent);
Instant compatibility with existing libraries!
executor.submit( ()->{ println(“Boo”); });
Lambda expressions may only appear in contexts where they can
undergo SAM conversion (assignment, method call/return, cast).
. . . . . .
60. double highestScore =
students.filter( (Student s)-> s.gradYear == 2010)
.map((Student s)-> s.score)
.max();
Since SAM-conversion uses target typing, the parameter types can
be left out!
. . . . . .
61. double highestScore =
students.filter( (Student s)-> s.gradYear == 2010)
.map((Student s)-> s.score)
.max();
Since SAM-conversion uses target typing, the parameter types can
be left out!
double highestScore =
students.filter(s -> s.gradYear == 2010)
.map(s -> s.score)
.max();
. . . . . .
62. So, lambda expressions are just a better syntax for inner
anonymous classes? I.e. more sugar?
. . . . . .
63. So, lambda expressions are just a better syntax for inner
anonymous classes? I.e. more sugar?
No! They cannot be!
. . . . . .
64. So, lambda expressions are just a better syntax for inner
anonymous classes? I.e. more sugar?
No! They cannot be!
To understand why, we need to understand how the JVM achieves
high performance today.
. . . . . .
65. static class Coord {
final int x, y;
Coord(int xx, int yy) { x=xx; y=yy; }
Coord add(Coord c) {
return new Coord(x+c.x,y+c.y);
}
double dist(Coord c) {
int dx = c.x-x;
int dy = c.y-y;
return Math.sqrt(dx*dx+dy*dy);
}
}
. . . . . .
66. public static class Matrix {
int a,b,c,d;
public Matrix(int aa, int bb, int cc, int dd) {
a = aa; b = bb; c = cc; d = dd;
}
public static Matrix scaler(int k) {
return new Matrix(k,0,0,k);
}
public Matrix mul(int k) {
return new Matrix(a*k,b*k,c*k,d*k);
}
Coord mul(Coord co) {
return new Coord(co.x*a+co.y*c,co.x*b+co.y*d);
}
}
. . . . . .
75. Fortunately a new JVM technology called the MethodHandle
is available for us.
A MethodHandle is (among many things) a root for inlining
that require no function!
A MethodHandle will be hidden behind the SAM-type
interface.
It can express new code using objects on the Java heap.
. . . . . .
76. Fortunately a new JVM technology called the MethodHandle
is available for us.
A MethodHandle is (among many things) a root for inlining
that require no function!
A MethodHandle will be hidden behind the SAM-type
interface.
It can express new code using objects on the Java heap.
This code can be efficiently optimized.
. . . . . .
77. Assume an interface to extract a value from an object:
interface Extractor<T, U> { U get(T element); }
And a new sort method on Collections using the Extractor:
public <T, U extends Comparable<...>>
void sortBy(Collection<T> coll, Extractor<T,U> ex) {...}
Then, pass a lambda expression that “wraps” a method call:
Collections.sortBy(people, p -> p.getLastName() );
SAM conversion types the lambda as
Extractor<Person,String>
sortBy() can pre-query last names, cache them, build indices...
. . . . . .
78. Is that the best we can do?
Collections.sortBy(people, p -> p.getLastName() );
Writing little wrapper lambdas will be a pain!
Lets reference a method directly!
Collections.sortBy(people, Person#getLastName );
Method reference introduced with # (not locked down yet!)
No need for () or parameter types in simple cases
. . . . . .
79. Another very common use case!
To bind user interface callbacks to your code:
save_button.addActionListener(this#save);
. . . . . .
80. Another very common use case!
To bind user interface callbacks to your code:
save_button.addActionListener(this#save);
Or even shorter:
save_button.addActionListener(#save);
. . . . . .
81. Recap
When code outgrows a lambda expression, write a method
and take a method reference
Lambda expressions and method references have SAM types
Work easily with existing libraries
Can specify parameter types explicitly if needed
Three kinds of method references (unbound/bound/static)
No field references (use method references to getters/setters)
. . . . . .
93. Lambda expressions - More concise
Better libraries - More abstract
Type inference
. . . . . .
94. Lambda expressions - More concise
Better libraries - More abstract
Type inference - Less ceremony
. . . . . .
95. Lambda expressions - More concise
Better libraries - More abstract
Type inference - Less ceremony
Method references
. . . . . .
96. Lambda expressions - More concise
Better libraries - More abstract
Type inference - Less ceremony
Method references - More reuse
. . . . . .
97. Lambda expressions - More concise
Better libraries - More abstract
Type inference - Less ceremony
Method references - More reuse
JVM support
. . . . . .
98. Lambda expressions - More concise
Better libraries - More abstract
Type inference - Less ceremony
Method references - More reuse
JVM support - More performance
. . . . . .