Preface
***
The Runnable interface we used often before cannot return results or throw out exception classes that can be checked. To make up for this, there's the Callable interface, which we're going to cover in this blog.
Callable interface
@FunctionalInterface public interface Callable<V> { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception; }
Similar to Runnable, the related method is declared, except that it has a return value and an exception is declared. The submit method in the thread pool can be used to submit a task thread that implements Callable.
Future interface
This interface can operate on the results of a specific Callable task, query, cancel, determine whether the Callable task is completed, and so on. It declares the following methods
public interface Future<V> { //Deletes a task, specifying whether an interrupt signal is sent directly to the target task at the time of deletion boolean cancel(boolean mayInterruptIfRunning); //Determine whether a task has been deleted boolean isCancelled(); //Determine if the task is completed boolean isDone(); //Get the results of the task execution, if not, it will block V get() throws InterruptedException, ExecutionException; //Gets the result of the task within the specified time, and returns automatically if it times out V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
The Future and Callable interfaces work together, and Future gets the results of the Callable task.
Future Get Results
/** * autor:liman * createtime:2021-12-01 * comment:Demonstrates how to use a Future */ @Slf4j public class OneFutureDemo { public static void main(String[] args) { ExecutorService service = Executors.newFixedThreadPool(10); //Subit of thread pool accepts methods that implement Callable interface Future<Integer> future = service.submit(new CallableTask()); try{ System.out.println(future.get()); }catch (Exception e){ e.printStackTrace(); } //Close Thread Pool service.shutdown(); } static class CallableTask implements Callable<Integer> { @Override public Integer call() throws Exception { Thread.sleep(3000); return new Random().nextInt(100); } } }
Get results from multiple tasks
/** * autor:liman * createtime:2021-12-01 * comment:Multiple Future tasks get results and accept batch results */ @Slf4j public class MultiFutureResultDemo { public static void main(String[] args) { ExecutorService threadPool = Executors.newFixedThreadPool(20); //Store the results of multiple tasks through a collection of Future s ArrayList<Future> resultFutureList = new ArrayList<>(); for (int i = 0; i < 20; i++) { Future<Integer> future = threadPool.submit(new CallableTask()); resultFutureList.add(future); } //Summarize the output, there are also some summary processing, such as what to sum up resultFutureList.stream().forEach(t->{ try { System.out.println(t.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } }); //Close Thread Pool threadPool.shutdown(); } static class CallableTask implements Callable<Integer> { @Override public Integer call() throws Exception { Thread.sleep(3000); return new Random().nextInt(100); } } }
Handling exceptions
Exception in getting results
If an exception occurs in the Callable task, although it has already occurred, it will not be thrown immediately, but will only be thrown when the get method is executed.
/** * autor:liman * createtime:2021-12-02 * comment: Demonstrates handling of thrown exceptions during get methods * Exceptions are not thrown immediately, they are only thrown at get time */ @Slf4j public class GetExceptionDemo { public static void main(String[] args) { ExecutorService threadPool = Executors.newFixedThreadPool(10); Future<Integer> result = threadPool.submit(new CallableTask()); try { //Hibernate a few times, at which point the child thread has had an exception, but has not thrown for (int i = 0; i < 5; i++) { Thread.sleep(5000); System.out.println("Dormant"+i+"second"); } boolean done = result.isDone();//Returns whether the corresponding result thread has completed execution System.out.println("isDone:"+done); //Exceptions can only be thrown when a get is executed. In fact, internal exceptions have already occurred, but exceptions are thrown when a get is executed. result.get(); } catch (InterruptedException ex) { System.out.println("InterruptedException"); ex.printStackTrace(); } catch (ExecutionException e) { System.out.println("ExecutionException"); e.printStackTrace(); } threadPool.shutdown(); } static class CallableTask implements Callable<Integer> { @Override public Integer call() throws Exception { //Analog throws an exception throw new IllegalArgumentException("callable Throw exception"); } } }
Get Task Timeout
TimeoutException can be thrown when getting the result of a task timeout. When handling this exception, the cancel method of the Future interface can be called. There is a boolean parameter when calling the cancel method. If this parameter is specified as false, it means that the running thread does not need to be interrupted by an interrupt signal, and true means that the interrupt signal needs to be sent to the target task.
/** * autor:liman * createtime:2021/12/4 * comment:Get Timeout handling of methods * After the timeout, it needs to be handled separately. Demonstrates whether cancel passes in true and false to interrupt an executing task */ @Slf4j public class GetTimeOutDemo { private static final Ad DEFAULT_AD = new Ad("Default Advertising Without Network"); private static final ExecutorService service = Executors.newFixedThreadPool(10); static class Ad { String name; public Ad(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Ad{" + "name='" + name + '\'' + '}'; } } /** * Tasks to Get Advertisements */ static class FetchAdTask implements Callable<Ad> { @Override public Ad call() throws Exception { try { Thread.sleep(3000); } catch (InterruptedException exception) { System.out.println("sleep Interrupted period"); return new Ad("Default ads when interrupted"); } return new Ad("Diamond lasts forever, a piece that lasts forever"); } } public void printAd() { Future<Ad> futureTask = service.submit(new FetchAdTask()); Ad ad = null; try { //A second to get results is not available, so it will time out ad = futureTask.get(1000, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { System.out.println("Interrupted"); e.printStackTrace(); } catch (ExecutionException e) { System.out.println("An exception occurred"); e.printStackTrace(); } catch (TimeoutException e) { System.out.println("Obtain Callable Result Timeout"); ad = new Ad("Default advertisement when interrupted"); //Timeout processing, the parameter passed in is false, indicating that no interrupt signal is required to interrupt the running thread, and true indicates that an interrupt is required boolean cancel = futureTask.cancel(true); System.out.println("cancel Results(Cancel running tasks normally)"+cancel); } System.out.println(ad); service.shutdown(); } public static void main(String[] args) { GetTimeOutDemo timeOutDemo = new GetTimeOutDemo(); timeOutDemo.printAd(); } }
When cancel, there are many situations
1. Calling cancel returns true if the task has not yet started.
2. Calling cancel will return false if the task has been completed, because the task can no longer be canceled
3. Call cancel if the task has already started, it will be judged by the mayInterruptIfRunning we passed in. Return true if the target task is interrupted successfully, false if the interrupt fails
FutureTask Get Results
All of our examples above get results through the Future interface returned by the thread pool.
You can see that FutureTask is an implementation class of Future, which is actually the only one that implements the RunnableFuture interface, which inherits both Runnable and Future. So FutureTask can be executed as a Runnable as a thread, and it can also be used as a Future to process the returned results of Callable.
Simple examples
/** * autor:liman * createtime:2021-12-01 * comment:Get results from Future Task and tasks */ public class FutureTaskDemo { public static void main(String[] args) { Task task = new Task(); FutureTask futureTask = new FutureTask<Integer>(task); // new Thread(futureTask).start();// Start futureTask as a thread ExecutorService service = Executors.newCachedThreadPool(); service.submit(futureTask);//Submit FutureTask to Thread Pool try { Object result = futureTask.get();//Get results directly from FutureTask System.out.println(result); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } class Task implements Callable<Integer>{ @Override public Integer call() throws Exception { System.out.println("Subthread is calculating"); Thread.sleep(3000); int sum =0; for (int i=0;i<=100;i++){ sum+=i; } return sum; } }
summary
Summary of simple uses of Future, Callable and FutureTask