Java Core | Concurrency in the Java Language and Platform | Fredrik Ohrstrom

Preview:

DESCRIPTION

2011-11-02 | 10:00 AM - 11:00 AM | VictoriaOracle 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.

Citation preview

. . . . . .

Concurrency in the Java Language and Platform

Fredrik ÖhrströmPrincipal Member of Technical Staff

JRockit+Hotspot

I have worked on the JRockit JVM for the last 6 six years and onthe 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.

. . . . . .

Concurrency in the Java Language and Platform

Fredrik ÖhrströmPrincipal Member of Technical Staff

JRockit+Hotspot

I have worked on the JRockit JVM for the last 6 six years and onthe 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.

. . . . . .

The following is intended to outline our general product direction.It is intended for information purposes only, and may not beincorporated into any contract. It is not a commitment to deliverany material, code, or functionality, and should not be relied uponin making purchasing decisions. The development, release, andtiming of any features or functionality described for Oracle’sproducts remains at the sole discretion of Oracle.

. . . . . .

In the beginning

I In 1974, Donald Knuth wrote a paper with the titleStructured Programming with goto statements.

I “In the late 1960s we witnessed a ’software crisis,’ whichmany people thought paradoxical because programming wassupposed to be easy.”

I It then takes him approx 60 pages to discuss the problemswith for-loops that were relevant at the time.

I Today, it is even more complicated!

. . . . . .

In the beginning

I In 1974, Donald Knuth wrote a paper with the titleStructured Programming with goto statements.

I “In the late 1960s we witnessed a ’software crisis,’ whichmany people thought paradoxical because programming wassupposed to be easy.”

I It then takes him approx 60 pages to discuss the problemswith for-loops that were relevant at the time.

I Today, it is even more complicated!

. . . . . .

In the beginning

I In 1974, Donald Knuth wrote a paper with the titleStructured Programming with goto statements.

I “In the late 1960s we witnessed a ’software crisis,’ whichmany people thought paradoxical because programming wassupposed to be easy.”

I It then takes him approx 60 pages to discuss the problemswith for-loops that were relevant at the time.

I Today, it is even more complicated!

. . . . . .

In the beginning

I In 1974, Donald Knuth wrote a paper with the titleStructured Programming with goto statements.

I “In the late 1960s we witnessed a ’software crisis,’ whichmany people thought paradoxical because programming wassupposed to be easy.”

I It then takes him approx 60 pages to discuss the problemswith for-loops that were relevant at the time.

I Today, it is even more complicated!

. . . . . .

double highestScore = 0.0;for (Student s : students) {if (s.gradYear == 2010) {

if (s.score > highestScore) {highestScore = s.score;

}}

}

I Traversal logic is encoded into bytecode.I Business logic is stateful.I Existing collections impose external iterationI For complex datasets the iterator becomes unnecessary

complex.

. . . . . .

double highestScore = 0.0;for (Student s : students) {if (s.gradYear == 2010) {

if (s.score > highestScore) {highestScore = s.score;

}}

}

I Traversal logic is encoded into bytecode.

I Business logic is stateful.I Existing collections impose external iterationI For complex datasets the iterator becomes unnecessary

complex.

. . . . . .

double highestScore = 0.0;for (Student s : students) {if (s.gradYear == 2010) {

if (s.score > highestScore) {highestScore = s.score;

}}

}

I Traversal logic is encoded into bytecode.I Business logic is stateful.

I Existing collections impose external iterationI For complex datasets the iterator becomes unnecessary

complex.

. . . . . .

double highestScore = 0.0;for (Student s : students) {if (s.gradYear == 2010) {

if (s.score > highestScore) {highestScore = s.score;

}}

}

I Traversal logic is encoded into bytecode.I Business logic is stateful.I Existing collections impose external iteration

I For complex datasets the iterator becomes unnecessarycomplex.

. . . . . .

double highestScore = 0.0;for (Student s : students) {if (s.gradYear == 2010) {

if (s.score > highestScore) {highestScore = s.score;

}}

}

I Traversal logic is encoded into bytecode.I Business logic is stateful.I Existing collections impose external iterationI For complex datasets the iterator becomes unnecessary

complex.

. . . . . .

This might sound like a theoretical exercise, but Oracle is affectedby the lack of concurrency in Javac, right at this very moment.

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

Developers need easy to use parallel libraries

I One of Java’s strengths has always been its librariesI Ideally the library should decide which strategy to use to

decompose the problem into parallel parts.I Obvious places are the collection classes, add parallel: filter,

sort, map/reduce, select, collect, detect, reject

I Why don’t we see more parallel libraries today?

. . . . . .

Developers need easy to use parallel libraries

I One of Java’s strengths has always been its librariesI Ideally the library should decide which strategy to use to

decompose the problem into parallel parts.I Obvious places are the collection classes, add parallel: filter,

sort, map/reduce, select, collect, detect, rejectI Why don’t we see more parallel libraries today?

. . . . . .

Without more language support forparallel idioms, people willinstinctively reach for serial idioms.

. . . . . .

Internal iteration (functional approach)double highestScore =students.filter(new Predicate<Student>() {

public boolean op(Student s) {return s.gradYear == 2010;

}}).map(new Extractor<Student,Double>() {

public Double extract(Student s) {return s.score;

}}).max();

I Not inherently serial.I Traversal logic completely inside collection.I Stateless!I Can be done in parallel!

. . . . . .

Internal iteration (functional approach)double highestScore =students.filter(new Predicate<Student>() {

public boolean op(Student s) {return s.gradYear == 2010;

}}).map(new Extractor<Student,Double>() {

public Double extract(Student s) {return s.score;

}}).max();

I Not inherently serial.

I Traversal logic completely inside collection.I Stateless!I Can be done in parallel!

. . . . . .

Internal iteration (functional approach)double highestScore =students.filter(new Predicate<Student>() {

public boolean op(Student s) {return s.gradYear == 2010;

}}).map(new Extractor<Student,Double>() {

public Double extract(Student s) {return s.score;

}}).max();

I Not inherently serial.I Traversal logic completely inside collection.

I Stateless!I Can be done in parallel!

. . . . . .

Internal iteration (functional approach)double highestScore =students.filter(new Predicate<Student>() {

public boolean op(Student s) {return s.gradYear == 2010;

}}).map(new Extractor<Student,Double>() {

public Double extract(Student s) {return s.score;

}}).max();

I Not inherently serial.I Traversal logic completely inside collection.I Stateless!

I Can be done in parallel!

. . . . . .

Internal iteration (functional approach)double highestScore =students.filter(new Predicate<Student>() {

public boolean op(Student s) {return s.gradYear == 2010;

}}).map(new Extractor<Student,Double>() {

public Double extract(Student s) {return s.score;

}}).max();

I Not inherently serial.I Traversal logic completely inside collection.I Stateless!I Can be done in parallel!

. . . . . .

The noise, the noisedouble highestScore =students.filter(new Predicate<Student>() {

public boolean op(Student s) {return s.gradYear == 2010;

}}).map(new Extractor<Student,Double>() {

public Double extract(Student s) {return s.score;

}}).max();

I Not inherently serial.I Traversal logic completely inside collection.I Stateless!I Can be done in parallel!

. . . . . .

Lambda Expressions

. . . . . .

Code as data?

double highestScore =students.filter(s -> s.gradYear == 2010).map(s -> s.score).max();

I An -> identifies a lambda expressionI Target type parametersI More than one parameter (a,b)->cI Body may be an expression or {statements}

. . . . . .

Code as data?

double highestScore =students.filter(s -> s.gradYear == 2010).map(s -> s.score).max();

I An -> identifies a lambda expression

I Target type parametersI More than one parameter (a,b)->cI Body may be an expression or {statements}

. . . . . .

Code as data?

double highestScore =students.filter(s -> s.gradYear == 2010).map(s -> s.score).max();

I An -> identifies a lambda expressionI Target type parameters

I More than one parameter (a,b)->cI Body may be an expression or {statements}

. . . . . .

Code as data?

double highestScore =students.filter(s -> s.gradYear == 2010).map(s -> s.score).max();

I An -> identifies a lambda expressionI Target type parametersI More than one parameter (a,b)->c

I Body may be an expression or {statements}

. . . . . .

Code as data?

double highestScore =students.filter(s -> s.gradYear == 2010).map(s -> s.score).max();

I An -> identifies a lambda expressionI Target type parametersI More than one parameter (a,b)->cI Body may be an expression or {statements}

. . . . . .

Code as data?

double highestScore =students.filter(s -> s.gradYear == 2010).map(s -> s.score).max();

I Code reads like the problem statement: Find the highest scoreof the students who graduated in 2010”

I Shorter than nested for loops, and potentially faster becausethe collection implementation determines how to iterate

I filter() method body can exploit representation knowledgeI Opportunities for lazy evaluation in filter() and map()I Opportunities for parallelism

. . . . . .

Code as data?

double highestScore =students.filter(s -> s.gradYear == 2010).map(s -> s.score).max();

I Code reads like the problem statement: Find the highest scoreof the students who graduated in 2010”

I Shorter than nested for loops, and potentially faster becausethe collection implementation determines how to iterate

I filter() method body can exploit representation knowledgeI Opportunities for lazy evaluation in filter() and map()I Opportunities for parallelism

. . . . . .

Code as data?

double highestScore =students.filter(s -> s.gradYear == 2010).map(s -> s.score).max();

I Code reads like the problem statement: Find the highest scoreof the students who graduated in 2010”

I Shorter than nested for loops, and potentially faster becausethe collection implementation determines how to iterate

I filter() method body can exploit representation knowledge

I Opportunities for lazy evaluation in filter() and map()I Opportunities for parallelism

. . . . . .

Code as data?

double highestScore =students.filter(s -> s.gradYear == 2010).map(s -> s.score).max();

I Code reads like the problem statement: Find the highest scoreof the students who graduated in 2010”

I Shorter than nested for loops, and potentially faster becausethe collection implementation determines how to iterate

I filter() method body can exploit representation knowledgeI Opportunities for lazy evaluation in filter() and map()

I Opportunities for parallelism

. . . . . .

Code as data?

double highestScore =students.filter(s -> s.gradYear == 2010).map(s -> s.score).max();

I Code reads like the problem statement: Find the highest scoreof the students who graduated in 2010”

I Shorter than nested for loops, and potentially faster becausethe collection implementation determines how to iterate

I filter() method body can exploit representation knowledgeI Opportunities for lazy evaluation in filter() and map()I Opportunities for parallelism

. . . . . .

Lambda Expressions

I The name comes from the lambda calculus created by Church(1936)

I Later explored by Steele and Sussman (1975-1980)I Thus by adding lambda expressions we have moved from 1974

(Knuth’s structured gotos) to 1975 (Scheme and lexicallambdas).

I Yay!

. . . . . .

Lambda Expressions

I The name comes from the lambda calculus created by Church(1936)

I Later explored by Steele and Sussman (1975-1980)

I Thus by adding lambda expressions we have moved from 1974(Knuth’s structured gotos) to 1975 (Scheme and lexicallambdas).

I Yay!

. . . . . .

Lambda Expressions

I The name comes from the lambda calculus created by Church(1936)

I Later explored by Steele and Sussman (1975-1980)I Thus by adding lambda expressions we have moved from 1974

(Knuth’s structured gotos) to 1975 (Scheme and lexicallambdas).

I Yay!

. . . . . .

Lambda Expressions

I The name comes from the lambda calculus created by Church(1936)

I Later explored by Steele and Sussman (1975-1980)I Thus by adding lambda expressions we have moved from 1974

(Knuth’s structured gotos) to 1975 (Scheme and lexicallambdas).

I Yay!

. . . . . .

Lambda Expressions

I A lambda expression is a lexically scoped anonymous method.

I Lexical scoping: can read variables from the lexicalenvironment, including “this”, unlike inner classes.

I No shadowing of lexical scope, unlike inner classes.I Not a member of any class, unlike inner classes.

. . . . . .

Lambda Expressions

I A lambda expression is a lexically scoped anonymous method.I Lexical scoping: can read variables from the lexical

environment, including “this”, unlike inner classes.I No shadowing of lexical scope, unlike inner classes.I Not a member of any class, unlike inner classes.

. . . . . .

Why were lambda expressions not added earlier to Java?

I After all, Steele who wrote the lambda papers in 1975, workedin the Java team for several years!

I The reason was the design decision that every memoryallocation should be visible as a “new” in Java source code.

I And the reason for this decision was that the hardware of thetime simply was not powerful enough.

I But now we have enough hardware to be able to spendsignificant cpu power on the optimization problems and GC tomake this less of a problem.

. . . . . .

Why were lambda expressions not added earlier to Java?

I After all, Steele who wrote the lambda papers in 1975, workedin the Java team for several years!

I The reason was the design decision that every memoryallocation should be visible as a “new” in Java source code.

I And the reason for this decision was that the hardware of thetime simply was not powerful enough.

I But now we have enough hardware to be able to spendsignificant cpu power on the optimization problems and GC tomake this less of a problem.

. . . . . .

Why were lambda expressions not added earlier to Java?

I After all, Steele who wrote the lambda papers in 1975, workedin the Java team for several years!

I The reason was the design decision that every memoryallocation should be visible as a “new” in Java source code.

I And the reason for this decision was that the hardware of thetime simply was not powerful enough.

I But now we have enough hardware to be able to spendsignificant cpu power on the optimization problems and GC tomake this less of a problem.

. . . . . .

Why were lambda expressions not added earlier to Java?

I After all, Steele who wrote the lambda papers in 1975, workedin the Java team for several years!

I The reason was the design decision that every memoryallocation should be visible as a “new” in Java source code.

I And the reason for this decision was that the hardware of thetime simply was not powerful enough.

I But now we have enough hardware to be able to spendsignificant cpu power on the optimization problems and GC tomake this less of a problem.

. . . . . .

Why were lambda expressions not added earlier to Java?

I After all, Steele who wrote the lambda papers in 1975, workedin the Java team for several years!

I The reason was the design decision that every memoryallocation should be visible as a “new” in Java source code.

I And the reason for this decision was that the hardware of thetime simply was not powerful enough.

I But now we have enough hardware to be able to spendsignificant cpu power on the optimization problems and GC tomake this less of a problem.

. . . . . .

Typing

. . . . . .

What is the type of a lambda expression?

s -> s.gradYear == 2010

I Morally, a function type from Student to booleanI But Java does not have function types....

. . . . . .

What is the type of a lambda expression?

s -> s.gradYear == 2010

I How would we write a function type?I How would it interact with boxing?I How would it interact with generics?I How would it describe checked exceptions?I Would it be possible to overload based on function type?

. . . . . .

In Java we use interfaces (and abstract classes) in two differentways:

I To specify behaviour: PrintWriter.println(),Student.setGrade(), GraphicsObject.render().

I 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. Forbackwards compatibility. When we have reified Java, then we canmore easily add real function types.

. . . . . .

In Java we use interfaces (and abstract classes) in two differentways:

I To specify behaviour: PrintWriter.println(),Student.setGrade(), GraphicsObject.render().

I 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. Forbackwards compatibility. When we have reified Java, then we canmore easily add real function types.

. . . . . .

In Java we use interfaces (and abstract classes) in two differentways:

I To specify behaviour: PrintWriter.println(),Student.setGrade(), GraphicsObject.render().

I 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. Forbackwards compatibility. When we have reified Java, then we canmore easily add real function types.

. . . . . .

In Java we use interfaces (and abstract classes) in two differentways:

I To specify behaviour: PrintWriter.println(),Student.setGrade(), GraphicsObject.render().

I 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. Forbackwards compatibility. When we have reified Java, then we canmore easily add real function types.

. . . . . .

Introducing SAM-types

A SAM-type is an interface or abstract class with a Single AbstractMethod

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

. . . . . .

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 canundergo SAM conversion (assignment, method call/return, cast).

. . . . . .

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 canundergo SAM conversion (assignment, method call/return, cast).

. . . . . .

double highestScore =students.filter( (Student s)-> s.gradYear == 2010).map((Student s)-> s.score).max();

Since SAM-conversion uses target typing, the parameter types canbe left out!

double highestScore =students.filter(s -> s.gradYear == 2010).map(s -> s.score).max();

. . . . . .

double highestScore =students.filter( (Student s)-> s.gradYear == 2010).map((Student s)-> s.score).max();

Since SAM-conversion uses target typing, the parameter types canbe left out!

double highestScore =students.filter(s -> s.gradYear == 2010).map(s -> s.score).max();

. . . . . .

So, lambda expressions are just a better syntax for inneranonymous classes? I.e. more sugar?

No! They cannot be!To understand why, we need to understand how the JVM achieveshigh performance today.

. . . . . .

So, lambda expressions are just a better syntax for inneranonymous classes? I.e. more sugar?

No! They cannot be!

To understand why, we need to understand how the JVM achieveshigh performance today.

. . . . . .

So, lambda expressions are just a better syntax for inneranonymous classes? I.e. more sugar?

No! They cannot be!To understand why, we need to understand how the JVM achieveshigh performance today.

. . . . . .

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

}}

. . . . . .

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

}

. . . . . .

Calculation

[33 00 33

]× 2×

[kk

]−

[2211

]=

[dxdy

]

answer =√

dx2 + dy2

public static double test(int k) {return Matrix.scaler(33).mul(2).mul(new Coord(k,k))

.dist(new Coord(22,11));}

. . . . . .

Calculation

[33 00 33

]× 2×

[kk

]−

[2211

]=

[dxdy

]

answer =√

dx2 + dy2

public static double test(int k) {return Matrix.scaler(33).mul(2).mul(new Coord(k,k))

.dist(new Coord(22,11));}

. . . . . .

Calculation

public static void main(String... args) {int i=0;double d = 0;for (;;) {

d = test(4711);if (++i > 1000000) {System.out.println("+d);i=0;

}}

}

. . . . . .

. . . . . .

x86_sub esp ← esp 4x86_mov edi ← eaxx86_test *[0xb722f000] eaxx86_imul eax ← eax 66x86_imul edi ← edi 66x86_mov ecx ← 22x86_sub ecx ← ecx eaxx86_mov esi ← 11x86_sub esi ← esi edix86_imul ecx ← ecx ecxx86_imul esi ← esi esix86_add ecx ← ecx esix86_cvtsi2sd xmm0 ← ecxx86_sqrtsd xmm0 ← xmm0x86_pop ecx esp ← espx86_retxmm0 esp

. . . . . .

Inlining implies great optimization opportunities

. . . . . .

But where to inline when using multiple cores?

. . . . . .

. . . . . .

I Fortunately a new JVM technology called the MethodHandleis available for us.

I A MethodHandle is (among many things) a root for inliningthat require no function!

I A MethodHandle will be hidden behind the SAM-typeinterface.

I It can express new code using objects on the Java heap.

I This code can be efficiently optimized.

. . . . . .

I Fortunately a new JVM technology called the MethodHandleis available for us.

I A MethodHandle is (among many things) a root for inliningthat require no function!

I A MethodHandle will be hidden behind the SAM-typeinterface.

I It can express new code using objects on the Java heap.I This code can be efficiently optimized.

. . . . . .

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

I SAM conversion types the lambda asExtractor<Person,String>

I sortBy() can pre-query last names, cache them, build indices...

. . . . . .

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

I Method reference introduced with # (not locked down yet!)I No need for () or parameter types in simple cases

. . . . . .

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

. . . . . .

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

. . . . . .

Recap

I When code outgrows a lambda expression, write a methodand take a method reference

I Lambda expressions and method references have SAM typesI Work easily with existing librariesI Can specify parameter types explicitly if neededI Three kinds of method references (unbound/bound/static)I No field references (use method references to getters/setters)

. . . . . .

Wrap up

. . . . . .

Collections.sort(people, new Comparator<Person>() {public int compare(Person x, Person y) {return x.getLastName().compareTo(y.getLastName());

}});

Collections.sort(people, (Person x, Person y)->x.getLastName().compareTo(y.getLastName()) );

Collections.sortBy(people, (Person p)-> p.getLastName() );

Collections.sortBy(people, p -> p.getLastName() );

Collections.sortBy(people, Person#getLastName);

. . . . . .

Collections.sort(people, new Comparator<Person>() {public int compare(Person x, Person y) {return x.getLastName().compareTo(y.getLastName());

}});

Collections.sort(people, (Person x, Person y)->x.getLastName().compareTo(y.getLastName()) );

Collections.sortBy(people, (Person p)-> p.getLastName() );

Collections.sortBy(people, p -> p.getLastName() );

Collections.sortBy(people, Person#getLastName);

. . . . . .

Collections.sort(people, new Comparator<Person>() {public int compare(Person x, Person y) {return x.getLastName().compareTo(y.getLastName());

}});

Collections.sort(people, (Person x, Person y)->x.getLastName().compareTo(y.getLastName()) );

Collections.sortBy(people, (Person p)-> p.getLastName() );

Collections.sortBy(people, p -> p.getLastName() );

Collections.sortBy(people, Person#getLastName);

. . . . . .

Collections.sort(people, new Comparator<Person>() {public int compare(Person x, Person y) {return x.getLastName().compareTo(y.getLastName());

}});

Collections.sort(people, (Person x, Person y)->x.getLastName().compareTo(y.getLastName()) );

Collections.sortBy(people, (Person p)-> p.getLastName() );

Collections.sortBy(people, p -> p.getLastName() );

Collections.sortBy(people, Person#getLastName);

. . . . . .

Collections.sort(people, new Comparator<Person>() {public int compare(Person x, Person y) {return x.getLastName().compareTo(y.getLastName());

}});

Collections.sort(people, (Person x, Person y)->x.getLastName().compareTo(y.getLastName()) );

Collections.sortBy(people, (Person p)-> p.getLastName() );

Collections.sortBy(people, p -> p.getLastName() );

Collections.sortBy(people, Person#getLastName);

. . . . . .

I Lambda expressions - More conciseI Better libraries - More abstractI Type inference - Less ceremonyI Method references - More reuseI JVM support - More performance

. . . . . .

I Lambda expressions

- More conciseI Better libraries - More abstractI Type inference - Less ceremonyI Method references - More reuseI JVM support - More performance

. . . . . .

I Lambda expressions - More concise

I Better libraries - More abstractI Type inference - Less ceremonyI Method references - More reuseI JVM support - More performance

. . . . . .

I Lambda expressions - More conciseI Better libraries

- More abstractI Type inference - Less ceremonyI Method references - More reuseI JVM support - More performance

. . . . . .

I Lambda expressions - More conciseI Better libraries - More abstract

I Type inference - Less ceremonyI Method references - More reuseI JVM support - More performance

. . . . . .

I Lambda expressions - More conciseI Better libraries - More abstractI Type inference

- Less ceremonyI Method references - More reuseI JVM support - More performance

. . . . . .

I Lambda expressions - More conciseI Better libraries - More abstractI Type inference - Less ceremony

I Method references - More reuseI JVM support - More performance

. . . . . .

I Lambda expressions - More conciseI Better libraries - More abstractI Type inference - Less ceremonyI Method references

- More reuseI JVM support - More performance

. . . . . .

I Lambda expressions - More conciseI Better libraries - More abstractI Type inference - Less ceremonyI Method references - More reuse

I JVM support - More performance

. . . . . .

I Lambda expressions - More conciseI Better libraries - More abstractI Type inference - Less ceremonyI Method references - More reuseI JVM support

- More performance

. . . . . .

I Lambda expressions - More conciseI Better libraries - More abstractI Type inference - Less ceremonyI Method references - More reuseI JVM support - More performance

. . . . . .

Thanks!