java语言Future、FutureTask的使用,对比Callable、Runable的执行过程(三)

 

前面两篇我们介绍了 线程的基本知识,Thread,Runnable,Callable,今天我们看看FutureTask的使用,为什么要介绍他呢,一方面他提供了线程执行结果的返回值,另外一方面和前面说的callable有些相通性



看看源码中FutureTask 的类继承关系:
public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V>

FutureTask类继承关系图

 

RunnableFuture接口中只定义了一个 run方法,

Runnable 接口我们都知道他只有一个 run方法,没有参数,没用返回值的方法

Future 接口提供了获取返回值的get方法,以及cancel方法。

Future接口 定义的5个方法

看看FutureTask的构造方法:

FutureTask的状态: 7种状态

    /**
     * The run state of this task, initially NEW.  The run state
     * transitions to a terminal state only in methods set,
     * setException, and cancel.  During completion, state may take on
     * transient values of COMPLETING (while outcome is being set) or
     * INTERRUPTING (only while interrupting the runner to satisfy a
     * cancel(true)). Transitions from these intermediate to final
     * states use cheaper ordered/lazy writes because values are unique
     * and cannot be further modified.
     *
     * Possible state transitions:
     * NEW -> COMPLETING -> NORMAL
     * NEW -> COMPLETING -> EXCEPTIONAL
     * NEW -> CANCELLED
     * NEW -> INTERRUPTING -> INTERRUPTED
     */
    private volatile int state;
    private static final int NEW          = 0;
    private static final int COMPLETING   = 1;
    private static final int NORMAL       = 2;
    private static final int EXCEPTIONAL  = 3;
    private static final int CANCELLED    = 4;
    private static final int INTERRUPTING = 5;
    private static final int INTERRUPTED  = 6;
Talk is cheap,show me your code!!!

public class FutureTaskDemo {

    static ExecutorService executor = Executors.newSingleThreadExecutor();

    public static void main(String[] args) {
        futureTaskDemo();
    }

    // 使用FutureTask,有返回值
    private static void futureTaskDemo() {
        Callable<Long> callable = new Callable<Long>() {
            @Override
            public Long call() throws Exception {
                MyLog.printTimeAndThread("callable");
                Thread.sleep(1000);
                return 200L;
            }
        };
        FutureTask<Long> futureTask = new FutureTask<Long>(callable);
        // 1. 通过把FutureTask 对象作为Thread参数构建Thread对象,来使用FutureTask
        /*Thread thread = new Thread(futureTask);
        thread.start();
        Long result = futureTask.get();
        System.out.println("result:" + result);
        */

        // 2. 通过把FutureTask 对象提交到线程池Executor中,来使用FutureTask
        executor.submit(futureTask);
        Long result = null;
        try {
            result = futureTask.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("result:" + result);

        System.out.println("==============================");

        // 2.1 当然上面这个 callable对象,相当于一个任务,可以直接丢入到executor线程池中处理, 如:
        Future<?> future = executor.submit(callable);
        try {
            System.out.println("result:" + future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        System.out.println("==============================");

        // 3. FutureTask 的构造方法,可以传入Runnable对象,以及一个返回值 对象,如:
        MyData val = new MyData();
        FutureTask<MyData> futureTask2 = new FutureTask<MyData>(new Runnable() {
            @Override
            public void run() {
                MyLog.printTimeAndThread("futureTask2");
                val.setValue(200);
            }
        }, val);

        Future<MyData> future2 = (Future<MyData>) executor.submit(futureTask2);
        MyData myData = null;
        try {
            myData = futureTask2.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("futureTask2 result:" + myData.getValue());
        executor.shutdown();
    }
}

class MyData {
    Integer value;
    public Integer getValue(){return value;}
    public void setValue(Integer val){this.value = val;}
}



public class MyLog {

    public static void sleepMillis(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void printTimeAndThread(String tag) {
        String result = new StringJoiner("\t|\t")
                .add(String.valueOf(System.currentTimeMillis()))
                .add(String.valueOf(Thread.currentThread().getId()))
                .add(Thread.currentThread().getName())
                .add(tag)
                .toString();
        System.out.println(result);
    }
}




运行结果:
1619077103371	|	12	|	pool-1-thread-1	|	callable
result:200
==============================
1619077104371	|	12	|	pool-1-thread-1	|	callable
result:200
==============================
1619077105374	|	12	|	pool-1-thread-1	|	futureTask2
futureTask2 result:200

这个运行结果,对照看下,应该很好理解就不多说了 

 

总结下:

FutureTask,和Callable 有些类似

优点: 可以获取到线程运行完后方法的返回值。 

这也是他的特性。但是要注意一旦调用get方法,调用线程就处于阻塞状态。

 

缺点

A: FutureTask一旦调用get方法,需要捕获异常,这个异常处理就恶心了,代码就不'美'了

Future 接口中 get方法定义:

V get() throws InterruptedException, ExecutionException;
Callable 接口 也有这个问题,异常处理,而且他更恶心,抛出的是一个更宽泛的异常:
Callable接口中 call方法定义:

 V call() throws Exception;

  B: 还有就是使用FutureTask之前,要先构建一个Callable对象,有没有更简便的方式呢——更简洁的方式使用多线程,同时可以获取到线程的返回值,同时异常处理更优雅些。

答案是肯定有的,我们可以看下一篇吧,下一篇我们会介绍CompletableFuture的使用

 

Callable、Runable、Future、FutureTask简单总结:

Callable - 接口,只有call方法,有结果的同步行为;
Runnable - 接口,只有run方法,无结果的同步行为;
FutureTask - 实现类,异步封装Callable/Runnable,依赖Executor执行 ,执行的返回值是 Future对象, 通过Future来get异步执行的结果;

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值