Friday, September 18, 2015

Java Concurrency Model - Part III - FutureTask

Note: Get the source code from https://github.com/sommukhopadhyay/FutureTask

In the last post we have seen about CoundownLatch. In this post i will discuss about FutureTask.

Callables & Future

The FiutureTask is "A cancellable asynchronous computation. This class provides a base implementation of Future, with methods to start and cancel a computation, query to see if the computation is complete, and retrieve the result of the computation."

Callables are just like Runnables to define the smallest unit of work called tasks. The difference between Callable and Runnable is that Runnable cannot return any result whereas Callable can return result of type Future. Later we can get the data from this return value.

Once we submit the Callable task to the Executor, it does not block and returns immediately. We can determine if the task is finished or not by using the isDone api. Once isDone returns TRUE, we can access the result from the future which was returned from submitting the task to the executor using the future.get() API. However, we must remember that get API is blocking and hence if the task is not completed when the get has been called, it will block the thread.

ExecutorService
Actually FutureTask is designed to be used through the ExecutorService interface and the classes that implement it. It is those classes that use FutureTask and fork the threads and create non-blocking Asynchronous background task. Executors typically manage a pool of threads so that we don't have to create threads manually. All the threads in a thread-pool can be reused.

Class ProductInfo



package com.somitsolutions.training.java.ExperimentationWithFutureTask;

public class ProductInfo {
 
 private String productName;
 private float productPrice;
 
 public ProductInfo(String productName, float productPrice){
  this.productName = productName;
  this.productPrice = productPrice;
 }
 
 public String getProductName() {
  return productName;
 }
 public void setProductName(String productName) {
  this.productName = productName;
 }
 public float getProductPrice() {
  return productPrice;
 }
 public void setProductPrice(float productPrice) {
  this.productPrice = productPrice;
 }
 
 
}

Class Preloader

package com.somitsolutions.training.java.ExperimentationWithFutureTask;


import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.Callable;

public class Preloader {
 
  static ExecutorService executor  = Executors.newFixedThreadPool(1);
  List<ProductInfo> productInfo = new ArrayList<ProductInfo>();
  //The difference between Callable & Runnable 
  //is that Callable can return a value (of type futuretask)
  private FutureTask<List<ProductInfo>> future = null;
  
  /*new FutureTask>(new LoadProductInfo());*/
    
  
  public List<ProductInfo> get(){
   //List retValue = null;
   try {
    //get is blocking
    productInfo =  future.get();
   } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (ExecutionException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   return productInfo;
  }
  
  public boolean cancel(){
   return future.cancel(true);
  }
  
  public boolean isDone(){
   return future.isDone();
  }
  
  //private final Thread thread = new Thread(future);
  
  public void start() {
   System.out.println("The task is being submitted now...");
   //submit will return immediately. So we can do the other work
   //in the main thread. Later we can check if the task is
   //finished or not using isDone method.
   future = (FutureTask<List<ProductInfo>>) (executor.submit(new LoadProductInfo())); 
   }
  
  //long running task
  private List<ProductInfo> loadProductInfo(){
   System.out.println(Thread.currentThread().getName());
   try {
    Thread.sleep(10000);
   } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   //As if this data we have got from 
   //the database
   for (int i = 0; i<100000; i++){
    ProductInfo productI = new ProductInfo(Integer.toString(i), i);
    productInfo.add(productI);
   }
   return productInfo;
  }
  //The difference between Callable & Runnable 
  //is that Callable can return a value (of type futuretask) 
  class LoadProductInfo implements Callable<List<ProductInfo>>{
 
   @Override
   public List<ProductInfo> call() throws Exception {
    // TODO Auto-generated method stub
    return loadProductInfo();
   }
   
  }
}

Class Main

package com.somitsolutions.training.java.ExperimentationWithFutureTask;

import java.util.List;

public class Main {
 public static void main(String[] args){
  List<ProductInfo> listOfProductInfo = null;
  System.out.println(Thread.currentThread().getName());
  
  Preloader preloader = new Preloader();
  
  //start getting the data in a background thread and
  //keep it for future use. while the background
  //thread is getting the data, we will continue
  //other task. later we can get this already fetched data
  //using future.get method. remember this get API is blocking
  preloader.start();
  
  /*//Do some other works... Here we are making the main thread sleep
  try {
   Thread.sleep(50000);
  } catch (InterruptedException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }*/
  
  int count = 0;
  while (!preloader.isDone()){
   System.out.println("Task is yet to be completed...");
   count++;
   try {
    Thread.sleep(1000);
   } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    Preloader.executor.shutdown();
    System.exit(0);
   } //sleep for 1 millisecond before checking again
   
   if (count == 100){//make the count == a low number say 8
    //to see the cancellation effect
    preloader.cancel();
    System.out.println("The task has been cancelled...");
    Preloader.executor.shutdown();
    System.exit(0);
    }
   }
  
   if(!preloader.cancel() && preloader.isDone()){
    listOfProductInfo = preloader.get();
   }
  
  System.out.println(listOfProductInfo.get(0).getProductName());
  System.out.print(listOfProductInfo.get(0).getProductPrice());
  
  Preloader.executor.shutdown();
 }
}

No comments: