飞道的博客

Java中Callable和Future

394人阅读  评论(0)

Java中为什么需要Callable

在java中有两种创建线程的方法:

一种是继承Thread类,重写run方法:


  
  1. public class TestMain {
  2.     public static void main( String[] args) {
  3.         MyThread t1 = new MyThread();
  4.        t1. start();
  5.   }
  6. }
  7. class MyThread extends Thread {
  8.     public void run( ) {
  9.         System. out. println( "MyThread running...");
  10.   }
  11. }
  12. 复制代码

第二种是使用Runnable创建一个线程:


  
  1. public class TestMain {
  2.     public static void main( String[] args) {
  3.         Runnable r1 = new Runnable() {
  4.             @Override
  5.             public void run( ) {
  6.                 System. out. println( "Thread created with runnable running...");
  7.           }
  8.       };
  9.         Thread t1 = new Thread(r1);
  10.        t1. start();
  11.   }
  12. }
  13. 复制代码

其实这两种方式,底层都是执行Thread类的run方法:

无论使用这里的哪种方式创建线程,都无法在线程结束时return一个返回值。但是在非常多的场景下,我们都需要在线程执行结束时,将执行的结果封装为一个返回值返回给主线程(或者调用者线程)。因此java在1.5版本时,在java.util.concurrent包引入了Callable接口,用于线程执行完时return一个返回值。

Callable和Runnable的区别

Runnable和Callable都是接口,分别定义如下:


  
  1. package java.lang;
  2. /**
  3. * The <code>Runnable</code> interface should be implemented by any
  4. * class whose instances are intended to be executed by a thread. The
  5. * class must define a method of no arguments called <code>run</code>.
  6. * <p>
  7. * @since   JDK1.0
  8. */
  9. @FunctionalInterface
  10. public interface Runnable {
  11.     public abstract void run ();
  12. }
  13. 复制代码

  
  1. package java.util.concurrent;
  2. /**
  3. * A task that returns a result and may throw an exception.
  4. * Implementors define a single method with no arguments called
  5. * {@code call}.
  6. *
  7. * <p>The {@code Callable} interface is similar to {@link
  8. * java.lang.Runnable}, in that both are designed for classes whose
  9. * instances are potentially executed by another thread. A
  10. * {@code Runnable}, however, does not return a result and cannot
  11. * throw a checked exception.
  12. *
  13. * <p>The {@link Executors} class contains utility methods to
  14. * convert from other common forms to {@code Callable} classes.
  15. *
  16. * @see Executor
  17. * @since 1.5
  18. * @author Doug Lea
  19. * @param <V> the result type of method {@code call}
  20. */
  21. @FunctionalInterface
  22. public interface Callable<V> {
  23.     /**
  24.     * @return computed result
  25.     * @throws Exception if unable to compute a result
  26.     */
  27.    V call () throws Exception;
  28. }
  29. 复制代码

可以看出,CallableRunnable主要有两点区别:

  1. 有返回值;
  2. 可以抛出异常(这里抛出的异常,会在future.get()时可以通过ExectionException捕获);

因此可以看出,Callable更加实用。这里举个Callable使用的例子:


  
  1. Callable callable = new Callable<Integer>() {
  2.   @Override
  3.   public Integer call () throws Exception {
  4.     int i = new Random().nextInt( 5);
  5.     try {
  6.      Thread.sleep(i * 1000);
  7.   } catch (InterruptedException e) {
  8.      e.printStackTrace();
  9.   }
  10.     return i;
  11. }
  12. };
  13. 复制代码

虽然Callable接口的call方法可以返回执行结果,但是有两个问题需要解决:

  1. 线程的创建只能通过Runnable,通过Callable又如何创建线程?
  2. 如何获取执行结果?

答案是FutureRunnableFuture

Future和RunnableFuture

Future是一个接口,看下定义:


  
  1. package java.util.concurrent;
  2. /**
  3. * A {@code Future} represents the result of an asynchronous
  4. * computation. Methods are provided to check if the computation is
  5. * complete, to wait for its completion, and to retrieve the result of
  6. * the computation. The result can only be retrieved using method
  7. * {@code get} when the computation has completed, blocking if
  8. * necessary until it is ready. Cancellation is performed by the
  9. * {@code cancel} method. Additional methods are provided to
  10. * determine if the task completed normally or was cancelled. Once a
  11. * computation has completed, the computation cannot be cancelled.
  12. * If you would like to use a {@code Future} for the sake
  13. * of cancellability but not provide a usable result, you can
  14. * declare types of the form {@code Future<?>} and
  15. * return {@code null} as a result of the underlying task.
  16. *
  17. * @see FutureTask
  18. * @see Executor
  19. * @since 1.5
  20. * @author Doug Lea
  21. * @param <V> The result type returned by this Future's {@code get} method
  22. */
  23. public interface Future<V> {
  24.     /**
  25.     * Attempts to cancel execution of this task. This attempt will
  26.     * fail if the task has already completed, has already been cancelled,
  27.     * or could not be cancelled for some other reason. If successful,
  28.     * and this task has not started when {@code cancel} is called,
  29.     * this task should never run. If the task has already started,
  30.     * then the {@code mayInterruptIfRunning} parameter determines
  31.     * whether the thread executing this task should be interrupted in
  32.     * an attempt to stop the task.
  33.     *
  34.     * <p>After this method returns, subsequent calls to {@link #isDone} will
  35.     * always return {@code true}. Subsequent calls to {@link #isCancelled}
  36.     * will always return {@code true} if this method returned {@code true}.
  37.     *
  38.     * @param mayInterruptIfRunning {@code true} if the thread executing this
  39.     * task should be interrupted; otherwise, in-progress tasks are allowed
  40.     * to complete
  41.     * @return {@code false} if the task could not be cancelled,
  42.     * typically because it has already completed normally;
  43.     * {@code true} otherwise
  44.     */
  45.     boolean cancel (boolean mayInterruptIfRunning);
  46.     /**
  47.     * Returns {@code true} if this task was cancelled before it completed
  48.     * normally.
  49.     *
  50.     * @return {@code true} if this task was cancelled before it completed
  51.     */
  52.     boolean isCancelled ();
  53.     /**
  54.     * Returns {@code true} if this task completed.
  55.     *
  56.     * Completion may be due to normal termination, an exception, or
  57.     * cancellation -- in all of these cases, this method will return
  58.     * {@code true}.
  59.     *
  60.     * @return {@code true} if this task completed
  61.     */
  62.     boolean isDone ();
  63.     /**
  64.     * Waits if necessary for the computation to complete, and then
  65.     * retrieves its result.
  66.     *
  67.     * @return the computed result
  68.     * @throws CancellationException if the computation was cancelled
  69.     * @throws ExecutionException if the computation threw an
  70.     * exception
  71.     * @throws InterruptedException if the current thread was interrupted
  72.     * while waiting
  73.     */
  74.    V get () throws InterruptedException, ExecutionException;
  75.     /**
  76.     * Waits if necessary for at most the given time for the computation
  77.     * to complete, and then retrieves its result, if available.
  78.     *
  79.     * @param timeout the maximum time to wait
  80.     * @param unit the time unit of the timeout argument
  81.     * @return the computed result
  82.     * @throws CancellationException if the computation was cancelled
  83.     * @throws ExecutionException if the computation threw an
  84.     * exception
  85.     * @throws InterruptedException if the current thread was interrupted
  86.     * while waiting
  87.     * @throws TimeoutException if the wait timed out
  88.     */
  89.    V get (long timeout, TimeUnit unit)
  90.         throws InterruptedException, ExecutionException, TimeoutException;
  91. }
  92. 复制代码

可以看出,Future可以用来表示线程的未来执行结果:一个容器,这个容器内将来存放的是线程的执行结果,线程执行完之前该容器内没有值,但是线程一旦执行成功(Callablecall方法返回之后),就会将结果存入该容器。从Future的接口定义可看出,Future不仅支持阻塞获取执行结果,还支持取消任务的执行,判断任务是否执行完成等。因此通过Future,主线程(或者调用者线程)可以跟进子现场的执行情况。

Callable其实和Runnable很像,都会执行一个任务,只不过Callable可以返回执行的结果。一般将执行结果封装到Future,调用者线程即可以通过Future获取Callable的执行结果了。因此,一般Callable会和Future搭配使用。

但是问题来了:java创建线程,需要Runnable,获取执行结果又需要Future。因此RunnableFuture来了:

可以看出,通过RunnableFuture,既可以创建线程,又可以获取线程的执行结果,当然RunnableFuture也是一个接口,我们一般情况下会使用它的具体实现类FutureTask

那可能又有人要问了,Callable又是如何建立联系的呢?看下FutureTask的使用方式就明白了:


  
  1. public class TestMain {
  2.     public static void main (String[] args) {
  3.         Callable callable = new Callable<Integer>() {
  4.             @Override
  5.             public Integer call () throws Exception {
  6.                 int i = new Random().nextInt( 5);
  7.                 try {
  8.                    Thread.sleep(i * 1000);
  9.               } catch (InterruptedException e) {
  10.                    e.printStackTrace();
  11.               }
  12.                 return i;
  13.           }
  14.       };
  15.         /**
  16.         * callable创建futureTask
  17.         * FutureTask实现了RunnableFuture接口,因此即是Runnable又是Future
  18.         * 作为Runnable可以传入Thread创建线程并执行
  19.         * 作为Future,可以用来获取执行的结果。
  20.         * 这里创建出来的futureTask对象有人称为"具柄"或者"存根",大家可以理解为用来获取线程执行结果的一个"引用"即可。
  21.         */
  22.        FutureTask<Integer> futureTask = new FutureTask<Integer>(callable);
  23.         // 作为Runnable使用
  24.         Thread thread = new Thread(futureTask);
  25.        thread.start();
  26.         try {
  27.             // 作为Future使用
  28.             Integer integer = futureTask.get();
  29.            System.out.println(integer);
  30.       } catch (InterruptedException e) {
  31.            e.printStackTrace();
  32.       } catch (ExecutionException e) {
  33.            e.printStackTrace();
  34.       }
  35.   }
  36. }
  37. 复制代码

因此FutureTaskCallableRunnable的桥梁。

不使用Callable和Future,仅使用Runnable实现相同功能

下面我们看下,如果不使用CallableFuture,仅使用Runnable如何实现返回值。


  
  1. public class TestMain {
  2.     public static void main( String[] args) {
  3.         MyRunnable myRunnable = new MyRunnable();
  4.         Thread t1 = new Thread(myRunnable);
  5.        t1. start();
  6.         Object o = myRunnable. get();
  7.         System. out. println(o);
  8.   }
  9. }
  10. class MyRunnable implements Runnable {
  11.     // 存储执行结果
  12.     private Object outCome = null;
  13.     @Override
  14.     public void run( ) {
  15.        int i = new Random(). nextInt( 5);
  16.         try {
  17.             Thread. sleep(i * 1000);
  18.       } catch ( InterruptedException e) {
  19.            e. printStackTrace();
  20.       }
  21.         // 存储执行结果
  22.        outCome = i;
  23.         // 产出结果后唤醒等待的get方法
  24.        synchronized ( this) {
  25.             notifyAll();
  26.       }
  27.   }
  28.     public synchronized Object get( ) {
  29.         while(outCome == null) {
  30.             try {
  31.                 // 等待产出结果
  32.                 wait();
  33.           } catch ( InterruptedException e) {
  34.                e. printStackTrace();
  35.           }
  36.       }
  37.         return outCome;
  38.   }
  39. }
  40. 复制代码

可以看出,通过Runnable实现更加麻烦,因此这也体现出了Callable+Future的优势。

本篇博文主要参考了Callable and Future in JavaFuture and FutureTask in java,感兴趣的话可以阅读原文。


转载:https://blog.csdn.net/BASK2311/article/details/127958849
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场