1. Preface

The first two articles briefly analyze  Future Interface and  FutureTask  This article will introduce  FutureTask  How to use .

2. FutureTask Use

FutureTask  There are two constructors , Pass in separately  Runnale  and  Callable  example . therefore  FutureTask  The use of is related to these two interfaces .

2.1 combination Callable

Callable Only one interface is defined  call() Method , And Runnable Of run() Methods compared , This method has a return value , Generic V Is the type of result to return , You can return the execution results of subtasks . If you want to get the result of thread execution, you can use  Callable , and  Callable  You can throw an exception . We define a simple task :

package cn.felord;
import java.util.concurrent.Callable;

/**
* @author felord.cn
* @since 2020/3/21 20:35
*/

public class MyTask implements Callable<Integer> {
   @Override
   public Integer call() {
       int total = 0;
       try {
           for (int i = 0; i < 10; i++) {
               System.out.println(" thread: " + Thread.currentThread().getName() + " i = " + i);
               Thread.sleep(1000);
               total += i;
           }
       } catch (InterruptedException e) {
           System.out.println("task is interrupted");
           // In case of an exception, you need to return after interrupt to ensure the end of the thread
           return 0;
       }
       return total;
   }
}

And then there's a  main  Template steps in method :

    public static void main(String[] args) {
       // First step : Declare specific computing tasks
       MyTask myTask = new MyTask();
       // The second step : Pass the task in to initialization FutureTask
       FutureTask<Integer> futureTask = new FutureTask<>(myTask);
       // The third step : take FutureTask Give it to a thread to execute
       Thread thread = new Thread(futureTask);

       thread.setName("future task thread");
       thread.start();

       try {
           Integer integer = futureTask.get();
           System.out.println("integer = " + integer);
       } catch (InterruptedException | ExecutionException e) {
           e.printStackTrace();
       }
       System.out.println(" task is over ");

   }

It seems to return the expected value after running  45 , There seems to be no problem . But when we use the timeout mechanism, that is, we change the code that gets the result in the above code to Integer integer = futureTask.get(5000, TimeUnit.MILLISECONDS); It turns out that :

 thread: future task thread i = 0
thread: future task thread i = 1
thread: future task thread i = 2
thread: future task thread i = 3
thread: future task thread i = 4
java.util.concurrent.TimeoutException
at java.util.concurrent.FutureTask.get(FutureTask.java:205)
at cn.felord.Test.main(Test.java:26)
thread: future task thread i = 5
task is over
thread: future task thread i = 6
thread: future task thread i = 7
thread: future task thread i = 8
thread: future task thread i = 9

We forced the task to time out , As a result, the computing thread of the task is still Computing , So we need to deal with the timeout exception, either interrupt the calculation or continue  get.

*

obtain FutureTask The result of timeout does not mean the end of the task . And it is generally not recommended to calculate tasks in the above way .

*

2.2 combination Runnable And define the results

The structure is like this :

  public FutureTask(Runnable runnable, V result) {
       this.callable = Executors.callable(runnable, result);
       this.state = NEW;       // ensure visibility of callable
 }

At first I thought this  result  It's through  runnable Calculated , But I was wrong :

   static final class RunnableAdapter<T> implements Callable<T> {
       final Runnable task;
       final T result;
       RunnableAdapter(Runnable task, T result) {
           this.task = task;
           this.result = result;
       }
       public T call() {
           task.run();
           return result;
       }
   }

This is  Executors.callable  The bottom layer of the method , We can't operate in threaded computing  result . don't quite understand  「JDK」  Why provide this " But it is of no damn use " The way of life . Run the given task and return 「 Given 」 Result ( It's the parameters you pass in result) That is, the execution of the task has nothing to do with the given result .

2.3 Use with thread pool

The above two general methods are only used in our research , Generally, it is recommended to use a specific thread pool for thread operation . So the following is the use of orthodoxy  FutureTask  Methods :

package cn.felord;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
* @author dax
* @since 2020/3/21 20:49
*/

public class Test {
   public static void main(String[] args) {
       // First step : Declare specific computing tasks
       MyTask myTask = new MyTask();

       // The second step : take FutureTask To the thread pool to execute
       // ExecutorService pool = Executors.newCachedThreadPool();
       // Custom thread pools are generally recommended perhaps Spring Provides a thread pool to prevent OOM At the same time, we should consider the shutdown strategy of thread pool
       ExecutorService pool = newThreadPool();

       try {
       // To get the results
           Integer integer = pool.submit(myTask).get();
           System.out.println("integer = " + integer);
       } catch (InterruptedException | ExecutionException e) {
           e.printStackTrace();
       }
   }


   private static ExecutorService newThreadPool() {
       ThreadFactory namedThreadFactory = new ThreadFactory() {
           final AtomicInteger sequence = new AtomicInteger(1);

           @Override
           public Thread newThread(Runnable r) {
               Thread thread = new Thread(r);
               int seq = this.sequence.getAndIncrement();
               thread.setName("future-task-thread" + (seq > 1 ? "-" + seq : ""));
               if (!thread.isDaemon()) {
                   thread.setDaemon(true);
               }

               return thread;
           }
       };
       return new ThreadPoolExecutor(5, 200,
               0L, TimeUnit.MILLISECONDS,
               new LinkedBlockingQueue<>(1024),
               namedThreadFactory,
               new ThreadPoolExecutor.AbortPolicy());
   }
}

Is it safer and simpler ?

3. Easy problems

  1. If specific  Callable  The exception goes 「try-catch」  Be sure to return a result , Otherwise, the thread will continue to execute .

  2. call get() Method will block until the task is finished .

  3. get(long timeout, TimeUnit unit) It is used to obtain the execution result within a certain period of time . If it times out, the following code will continue to execute , At the same time, computation is not interrupted .

  4. cancel(boolean mayInterruptIfRunning) It doesn't have to interrupt the mission immediately , Need to call  isDone perhaps  isCancelled  Judge , What's more secure is Calling task thread  isInterrupted()  Judge the interrupt state .

4. summary

Today, yes  FutureTask  How to use it is listed , At the same time, some misunderstandings in use are explained , I hope it helped you . If you have any questions, you can leave a message to discuss .