-
CallableStaticPL/JAVA 2019. 8. 21. 22:39
1. Overview
Whereas Runnable object can not return a result, Callable return a result and throw a checked exception in spite of being executed inside a thread likewise.
2. Description
2.1 Callable
Callable interface has a single method call() which is meant to contain the code is executed by a thread.
Callable<String> callable = new Callable<String>() { @Override public String call() throws Exception { // Perform some computation Thread.sleep(2000); return "Return some result"; } };
Unlike Runnable, Callable don't need to surround Thread.sleep() by a try/catch block because a Callable can throw a checked exception. lambda expression looks like this.
Callable<String> callable = () -> { // Perform some computation Thread.sleep(2000); return "Return some result"; };
2.2 Future
Future represents the result of a computation that will be completed at a later point in time in future.
import java.util.concurrent.*; public class FutureAndCallableExample { public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService executorService = Executors.newSingleThreadExecutor(); Callable<String> callable = () -> { // Perform some computation System.out.println("Entered Callable"); Thread.sleep(2000); return "Hello from Callable"; }; System.out.println("Submitting Callable"); Future<String> future = executorService.submit(callable); // This line executes immediately System.out.println("Do something else while callable is getting executed"); System.out.println("Retrieve the result of the future"); // Future.get() blocks until the result is available String result = future.get(); System.out.println(result); executorService.shutdown(); } }
2.3 ExecutorService.submit()
ExecutorService.submit() method returns immediately and gives you a Future. Once you have obtained a future, you can execute other tasks in parallel while your submitted task is executing.
2.4 Future.get()
Use future.get() method to retrieve the result of the future. But get method blocks until the task is completed.
2.5 Future.isDone()
The Future API also provides an isDone() method to check whether the task is completed or not
import java.util.concurrent.*; public class FutureIsDoneExample { public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService executorService = Executors.newSingleThreadExecutor(); Future<String> future = executorService.submit(() -> { Thread.sleep(2000); return "Hello from Callable"; }); while(!future.isDone()) { System.out.println("Task is still not done..."); Thread.sleep(200); } System.out.println("Task completed! Retrieving the result"); String result = future.get(); System.out.println(result); executorService.shutdown(); } }
2.6 Future.cancel()
Invoking Future.cancel() attempt to cancel the execution of the task and returns true if it is canceled successfully, otherwise, it returns false. And it accepts the boolean argument. if that's true, then the task will be interrupted on the thread, or not that task will be allowed to complete.
import java.util.concurrent.*; public class FutureCancelExample { public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService executorService = Executors.newSingleThreadExecutor(); long startTime = System.nanoTime(); Future<String> future = executorService.submit(() -> { Thread.sleep(2000); return "Hello from Callable"; }); while(!future.isDone()) { System.out.println("Task is still not done..."); Thread.sleep(200); double elapsedTimeInSec = (System.nanoTime() - startTime)/1000000000.0; if(elapsedTimeInSec > 1) { future.cancel(true); } } System.out.println("Task completed! Retrieving the result"); String result = future.get(); System.out.println(result); executorService.shutdown(); } }
Above future.get() throws an exception because the task is canceled. If the task is canceled, able to handle this fact by checking whether the future is canceled before retrieving the result.
if(!future.isCancelled()) { System.out.println("Task completed! Retrieving the result"); String result = future.get(); System.out.println(result); } else { System.out.println("Task was cancelled"); }
2.7 get Timeouts
To guard future.get() will block forever when a callable task is not responding which will make the application unresponsive, you can add a timeout in the get() method
future.get(1, TimeUnit.SECONDS);
This will throw a TimeoutException if the task is not completed within the specified time.
2.8 ExecutorService.invokeAll()
invodeAll() accepts a collection of Callables and returns a list of Futures. And it will also block until all the Futures are complete.
import java.util.Arrays; import java.util.List; import java.util.concurrent.*; public class InvokeAllExample { public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService executorService = Executors.newFixedThreadPool(5); Callable<String> task1 = () -> { Thread.sleep(2000); return "Result of Task1"; }; Callable<String> task2 = () -> { Thread.sleep(1000); return "Result of Task2"; }; Callable<String> task3 = () -> { Thread.sleep(5000); return "Result of Task3"; }; List<Callable<String>> taskList = Arrays.asList(task1, task2, task3); List<Future<String>> futures = executorService.invokeAll(taskList); for(Future<String> future: futures) { // The result is printed only after all the futures are complete. (i.e. after 5 seconds) System.out.println(future.get()); } executorService.shutdown(); } }
2.9 ExecutorService.invokeAny()
invodeAny() accepts a collection of Callables and returns the result of the fastest Callable which is not Future.
import java.util.Arrays; import java.util.List; import java.util.concurrent.*; public class InvokeAnyExample { public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService executorService = Executors.newFixedThreadPool(5); Callable<String> task1 = () -> { Thread.sleep(2000); return "Result of Task1"; }; Callable<String> task2 = () -> { Thread.sleep(1000); return "Result of Task2"; }; Callable<String> task3 = () -> { Thread.sleep(5000); return "Result of Task3"; }; // Returns the result of the fastest callable. (task2 in this case) String result = executorService.invokeAny(Arrays.asList(task1, task2, task3)); System.out.println(result); executorService.shutdown(); } }
3. Example
class Processor implements Callable<String> { private int id; public Processor(int id){ this.id = id; } @Override public String call() throws Exception { Thread.sleep(1000); return "Id: "+this.id; } } public class Demo { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(2); List<Future<String>> list = new ArrayList<>(); for(int i=0;i<5;i++){ Future<String> future = executorService.submit(new Processor(i+1)); list.add(future); } for(Future<String> future : list){ try{ System.out.println(future.get()); }catch(Exception e){ e.printStackTrace(); } } executorService.shutdown(); } }
4. References
https://www.callicoder.com/java-callable-and-future-tutorial/
'StaticPL > JAVA' 카테고리의 다른 글
Garbage Collection (GC) (0) 2019.08.23 JDK, JRI, JVM, and Classloader (0) 2019.08.23 Executors Framework and Thread Pools (0) 2019.08.21 CompletableFuture, and Future (0) 2019.08.20 Threadlocal (0) 2019.08.19