Understanding Java 8 Lambda expressions with working examples -- part 3
Example 4: Runnable interface and Thread class to create new threads via Lambda expressions
The Java API Runnable interface in Java 8 has been annotated with @FunctionalInterface, which means you can only have 1 abstract method.
@FunctionalInterface public interface Runnable { public abstract void run(); }
public class Thread implements Runnable { //..........skipped }
Step 1: Here is Lambda expression in action to create new threads.
package com.java8.examples; import java.util.concurrent.atomic.LongAdder; public class ThreadTest { private static LongAdder counter = new LongAdder(); public static void main(String[] args) { new Thread(() -> { counter.increment(); System.out.println(Thread.currentThread().getName() + " count -- " + counter.longValue()); }).start(); Thread t2 = new Thread(() -> { counter.increment(); System.out.println(Thread.currentThread().getName() + " count -- " + counter.longValue()); }); t2.start(); } }
The output:
Thread-0 count -- 1 Thread-1 count -- 2
Example 5: The free variables in lambda expressions are not thread-safe.
A lambda expression has 3 aspects:
- Parameters
- A block of code or body
- Values for the free variables. These are the variables that are not parameters, and not defined inside the closure.
The anonymous inner classes can only access variables defined outside if they are marked final. Otherwise you will get a compile error. The lambda expression has relaxed the requirement of variables being final, but the "free variables" that you use need to be either final or effectively final.
package com.java8.examples; import java.util.concurrent.atomic.LongAdder; public class ThreadTest { private static LongAdder counter = new LongAdder(); public static void main(String[] args) { int counterLocal = 0; //local free variable new Thread(() -> { counter.increment(); //local variables referenced from a lambda expression must be final or effectively final counterLocal++; //Line A: illegal. compile-error System.out.println(counterLocal); // Line B: Ok, as counterLocal is not mutated int a = counterLocal; //Line C: Ok, as counterLocal is not mutated System.out.println(Thread.currentThread().getName() + " count -- " + counter.longValue()); }).start(); } }
The above code gives compile error due to Line A. If you comment out Line A, the above code will compile, and Line B and Line C are ok as they use the variable counterLocal as read only and it is effectively final.
So, mutating free variables in a lambda expression is not thread-safe. The prohibition against mutation only holds true for local variables as explained above. You can still use an instance or static variable, but the compiler won't warn you, but you can have thread-safety issues.
Example 6: Understanding scope of "this" keyword
Will the toString( ) method invocation shown below invokes ThreadTest's toString( ) method or Thread class's toString( ) method?
package com.java8.examples; import java.util.concurrent.atomic.LongAdder; public class ThreadTest { public static void main(String[] args) { new ThreadTest().execute(); } public void execute() { new Thread(() -> { System.out.println(this.toString()); }).start(); } @Override public String toString() { return "ThreadTest class toString()"; } }
Output is:
ThreadTest class toString()
So, there is nothing special about using the "This" keyword in lambda expressions. The scope of lambda expression is nested inside the execute( ) method, and this has the same meaning anywhere inside the execute method.
More on Java 8 functional interfaces and Lambda expressions
- Understanding Java 8 Lambda expressions : function interfaces and Lambda expressions
- Understanding Java 8 Lambda expressions : Iterable interface is now functional with forEach( ) method
Labels: Java 8, Java Lambda expressions
0 Comments:
Post a Comment
Subscribe to Post Comments [Atom]
<< Home