Google

Apr 8, 2014

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:

  1. Parameters
  2. A block of code or body
  3. 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

Labels: ,

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home