Callable、Runnable,Future和FutureTask之间关系

创建线程的两种方式:继承Thread类 或 实现Runnable接口,重写run方法。

Thread类本身也实现了Runnable接口,Runnable接口源码:

run方法是无返回值的,所以在JDK1.5出现了Callable接口


 关系类图


Callable

Callable接口源码

Callable是一个函数式接口(接口中仅有一个方法),也是一个泛型接口,返回值类型和泛型一致


Future

Future接口源码

cancel:取消任务的执行,如果任务已完成或已被取消,则返回false

isCancelled:判断任务是否被取消

isDone:判断任务是否完成

get():阻塞获取任务的执行结果

get(long timeout, TimeUnit unit):在规定的时间内,阻塞获取任务的执行结果

Future接口提供了取消任务,任务状态查询,任务结果获取的能力;

Future机制就是为了解决多线程返回值的问题;


RunnableFuture

RunnableFuture接口源码

RunnableFuture继承了Runnable和Future两个接口,也就同时具备其两个接口的功能


FutureTask

FutureTask是真正工作的处理类,实现了RunnableFuture接口,而RunnableFuture接口继承了Runnable和Future接口,所以FutureTask既可以作为Runnable被Thread执行,也可以获取Future异步执行的结果;

FutureTask两个构造方法,一个接收Callable的参数实例,另一个接收Runnable的参数实例

 

 

 当传入的参数是Runnable时,通过Executors.callable(runnable, result)方法将其转成Callable类型(最终都是执行Callable类型的任务),返回值类型为V(指定的泛型类型)

RunnableAdapter适配器


 FutureTask-demo示例

ExecutorService线程池接口中,sumbit方法即定义了Runnable入参类型,也定义了Callable入参类型

package com.example.demo.test;

import java.util.concurrent.*;

public class RunnableFutureTest {

    private static ExecutorService pool = Executors.newFixedThreadPool(2);

    public static void main(String[] args) throws Exception {
        testFuture(20);
        testRunnable(20);
    }

    /**
     * new Thread().start()新建一个线程,启动线程(实际执行run方法,无返回值)
     */
    static void testRunnable(int number) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("runnable sum:" + calcOneToTargetSum(number));
            }
        }).start();
    }

    /**
     * Runnable:实现run(),无返回值,不可以抛出异常
     * Callable:实现call(),有返回值,可以抛出异常
     * Runnable可以直接交给Thread来执行
     * Callable不可以直接交给Thread来执行,一般交给ExecutorService执行
     */
    static void testFuture(int number) {
        try {
            Future<?> result1 = pool.submit(new Runnable() {
                @Override
                public void run() {
                    calcOneToTargetSum(number);
                }
            });
            // 无返回值,get()会阻塞
            System.out.println("result1:" + result1.get());

            Future<Integer> result2 = pool.submit(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    return calcOneToTargetSum(number);
                }
            });
            // 有返回值,get()会阻塞
            System.out.println("result2:" + result2.get());

            FutureTask<Integer> futureTask1 = new FutureTask<>(new Runnable() {
                @Override
                public void run() {
                    calcOneToTargetSum(number);
                }
            }, calcOneToTargetSum(number));
            pool.submit(futureTask1);
            // 有返回值,get()会阻塞
            System.out.println("result3:" + futureTask1.get());

            FutureTask<Integer> futureTask2 = new FutureTask<>(new Runnable() {
                @Override
                public void run() {
                    calcOneToTargetSum(number);
                }
            }, calcOneToTargetSum(number));
            pool.submit(futureTask2);
            // Executors.callable会将Runnable转换为Callable,固有返回值,get()会阻塞
            System.out.println("result4:" + futureTask2.get());
            
       // FutureTask实现了RunnableFuture接口,RunnableFuture接口继承了Runnable接口,因此可以作为Thread构造参数传入
new Thread(futureTask2).start(); // 无返回值,get()会阻塞 System.out.println("result5:" + futureTask2.get()); } catch (Exception e) { e.printStackTrace(); } finally { pool.shutdown(); } } static int calcOneToTargetSum(int number) { int sum = 0; for (int i = 0; i < number; i++) { sum += i; } try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } return sum; } }


Runnable和Callable接口的区别:

  • Runnable定义的方法是run(),而Callable定义的方法是call()
  • Runnable定义的方法是run()无返回值,而Callable定义的方法是call()有返回值
  • Runnable定义的方法是run()不能抛出异常,而Callable定义的方法是call()可以抛出异常
posted @ 2021-05-24 12:25  coolw  阅读(275)  评论(0编辑  收藏  举报