Java concurrent Callable and Future (create thread)

      Generally speaking, there are two main ways for us to create threads: ① directly inherit Thread; ② implement Runnable interface. However, these two methods have no parameters and return values, which means that the execution result will not be returned after the task is executed. If you want to get the execution result, you need to communicate through shared variables or threads, which is bound to be very troublesome. Since Java 1.5, the Callable and Future interfaces are provided, through which the task result (return value) can be easily achieved after completing the task.

      First, let's take a look at java.lang.Runnable. There is only one run() method declared in this interface. The return value of this method is of type void, and there will be no return value after the task is executed:

public interface Runnable {
    public abstract void run();
}

The Callable interface is similar to Runnable, but has a return value. The Callable interface is located in the java.util.concurrent package, and this interface has only one method call():

public interface Callable<V> {
    V call() throws Exception;
}

The type parameter is the type of the return value. For example, Callable<Integer> represents a one-step computation that eventually returns an Integer object. In general, Callable is used with ExecutorService, and several overloaded submit methods are declared in the ExecutorService interface:

        <T> Future<T> submit(Callable<T> task);

        <T> Future<T> submit(Runnable task, T result);

        Future<?> submit(Runnable task);

      The Future interface is to cancel the execution result of a specific Callable task, query whether it is completed, and obtain the result. If necessary, the execution result can be obtained through the get method, which will block until the task returns the result. This interface is located in the java.util.concurrent package, and the interface has five methods (these five methods can complete three functions: 1. Determine whether the task is completed; 2. Can interrupt the task; 3. Obtain the task execution result):

public interface Future<V> {
    void cancel(boolean mayInterrupt);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}
cancel method: used to cancel the task, if the task has not started, it is canceled and will not start again, if the task is running, then the mayInterrupt parameter is true, the task is interrupted. Returns true if the task is canceled successfully, and false if the cancel task fails. If the task has been completed, whether mayInterrupt is true or false, this method must return false; if the task is executing, if mayInterrupt is set to true, it will return true, if mayInterrupt is set to false, it will return false; if the task has not been executed, Then whether mayInterrupt is true or false, it will definitely return true.
isCancelled method: Indicates whether the task was canceled successfully, and returns true if the task is canceled successfully before the task is completed normally.
isDone method: Indicates whether the task has been completed, and returns true if the task is completed;
get() method: used to obtain the execution result, this method will block and will not return until the task is completed;

get(long timeout, TimeUnit unit) method: used to obtain the execution result. If the result is not obtained within the specified time, it will directly return null.

      Since Future is just an interface, it cannot be used directly to create objects. Fortunately, there is a FutureTask wrapper that can convert Callable into Future and Runnable. It implements both interfaces at the same time, so FutureTask can be executed by threads as Runnable, and can also be used as Runnable. The return value of Callable can be obtained as a Future. FutureTask provides 2 constructors:

public FutureTask(Callable<V> callable) {
}
public FutureTask(Runnable runnable, V result) {
}

The following is an example of how to use Callable and Future. The following example is derived from Java Core Technology Volume I - Concurrency. The main function of this example is to search all files under the specified path and files under the sub-path according to the input file path and keywords. Whether to include the specified keyword, and count the number of matching files.

import java.io. *;
import java.util. *;
import java.util.concurrent.*;

public class FutureTest
{
   public static void main(String[] args)
   {
      try (Scanner in = new Scanner(System.in))
      {
         System.out.print("Enter base directory (e.g. /usr/local/jdk5.0/src): ");
         String directory = in.nextLine();
         System.out.print("Enter keyword (e.g. volatile): ");
         String keyword = in.nextLine();
   
         MatchCounter counter = new MatchCounter(new File(directory), keyword);
         FutureTask<Integer> task = new FutureTask<>(counter);
         Thread t = new Thread(task);
         t.start();
         try
         {
            System.out.println(task.get() + " matching files.");
         }
         catch (ExecutionException e)
         {
            e.printStackTrace ();
         }
         catch (InterruptedException e)
         {
         }
      }
   }
}

/**
 * This task counts the files in a directory and its subdirectories that contain a given keyword.
 */
class MatchCounter implements Callable<Integer>
{
   private File directory;
   private String keyword;

   /**
    * Constructs a MatchCounter.
    * @param directory the directory in which to start the search
    * @param keyword the keyword to look for
    */
   public MatchCounter(File directory, String keyword)
   {
      this.directory = directory;
      this.keyword = keyword;
   }

   public Integer call()
   {
      int count = 0;
      try
      {
         File[] files = directory.listFiles();
         List<Future<Integer>> results = new ArrayList<>();

         for (File file : files)
            if (file.isDirectory())
            {
               MatchCounter counter = new MatchCounter(file, keyword);
               FutureTask<Integer> task = new FutureTask<>(counter);
               results.add(task);
               Thread t = new Thread(task);
               t.start();
            }
            else
            {
               if (search(file)) count++;
            }

         for (Future<Integer> result : results)
            try
            {
               count += result.get();
            }
            catch (ExecutionException e)
            {
               e.printStackTrace ();
            }
      }
      catch (InterruptedException e)
      {
      }
      return count;
   }

   /**
    * Searches a file for a given keyword.
    * @param file the file to search
    * @return true if the keyword is contained in the file
    */
   public boolean search(File file)
   {
      try
      {
         try (Scanner in = new Scanner(file, "UTF-8"))
         {
            boolean found = false;
            while (!found && in.hasNextLine())
            {
               String line = in.nextLine();
               if (line.contains(keyword)) found = true;
            }
            return found;
         }
      }
      catch (IOException e)
      {
         return false;
      }
   }
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325898929&siteId=291194637