Java Concurrency Tool Learning - Future, Callable and FutureTask

Keywords: Java Back-end

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

Posted by ryclem on Sun, 05 Dec 2021 15:43:08 -0800