Android并发二三事之Callable,Future,FutureTask

Android 并发的第二篇,本篇还是要介绍Java 方向并发相关的知识点。

主要涉及Callable与 Runnable 的关系。 Callable 如何使用, Future 概念, Future 如何使用以及 Future 的实现类 FutureTask。

最后介绍如何利用Future 实现在子线程中开启子线程去请求网络。

一、Callable :

public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;

}

Callable 本身是一个泛型接口, call()方法返回的类型,就是V的类型。
可能大家更熟悉的是:

Runnable接口:

public interface Runnable {

    /**
     * Starts executing the active part of the class' code. This method is
     * called when a thread is started that has been created with a class which
     * implements {@code Runnable}.
     */
    public void run();
}

Callable 类似于Runnable 接口,用来封装一个任务,可以运行在一个线程之中。
只不过 Runnable 的 run() 方法没有返回值, 而 call() 方法具有返回值。

不过不同于 Runnable 的是,Callable 无法被封装到Thread中执行。
一般Callable 会与线程池一起使用。

在 ExecutorService 中有具有一下几个接口:

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


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


Future<?> submit(Runnable task);

在上一篇博客中,我们介绍了利用Executors 提供的线程池,执行联网等耗时任务:

public void request() {

        ExecutorService executor = Executors.newFixedThreadPool(5);
        executor.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    //请求网络等。
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

    }

这里其实除了execute() 方法还有一个方法可以用,那就是submit()方法。
只不过submit()方法是有返回值的,返回值就是Future 。

二、Future 接口:

public interface Future<V> {


    boolean cancel(boolean mayInterruptIfRunning);


    boolean isCancelled();


    boolean isDone();


    V get() throws InterruptedException, ExecutionException;


    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

Future 可以用来获取Runnable , Callable 任务的执行结果,查询等。
一般Future 模式可以这样理解:
我有一个任务交给Future,让Future 替我去完成。在这个期间我可以做别的事情,一段时间之后,我便可以从Future 那里获取结果。
Future 一般和线程池一起使用,利用 ExecutorService 的 submit() 方法,就可以返回一个Future 对象。

Future接口提供方法来检测任务是否被执行完,等待任务执行完获得结果,也可以设置任务执行的超时时间。这个设置超时的方法就是实现Java程 序执行超时的关键。

接口的方法介绍如下:
boolean cancel (boolean mayInterruptIfRunning) 取消任务的执行。参数指定是否立即中断任务执行,或者等等任务结束

boolean isCancelled () 任务是否已经取消,任务正常完成前将其取消,则返回 true

boolean isDone () 任务是否已经完成。需要注意的是如果任务正常终止、异常或取消,都将返回true

V get () throws InterruptedException, ExecutionException 等待任务执行结束,然后获得V类型的结果。InterruptedException 线程被中断异常, ExecutionException任务执行异常,如果任务被取消,还会抛出CancellationException

V get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException 同上面的get功能一样,多了设置超时时间。参数timeout指定超时时间,uint指定时间的单位,在枚举类TimeUnit中有相关的定义。如果计 算超时,将抛出TimeoutException

三、FutureTask:

FutureTask 即为Future 的实现, FutureTask 不仅实现了 Future, 还实现了 Runnable 所以其也可以作为一个Runnable 提交给线程池。

submit() 方法为我们返回的其实就是 FutureTask 对象。

四、以下为利用Future获取数据的例子:

//请求数据
public void requestCar() {

        ExecutorService executor = Executors.newFixedThreadPool(5);
        Future<ResponInfo> future = executor.submit(new Task());
        try{
            ResponInfo info = future.get();
            Log.d(TAG,"result : "+info.getName());
        }catch (Exception e){

        }

    }

//获取数据
 private ResponInfo requestInfo() {
        try {
        //模拟连接网络耗时
            Thread.sleep(3000);
        }catch (Exception e) {

        }
        return new ResponInfo("BMW", 2000);
    }
//自定义Callable
public class Task implements Callable<ResponInfo> {

        @Override
        public ResponInfo call() throws Exception {

            return requestInfo();
        }
    }
//数据实体
    public class ResponInfo {

        private String name;
        private long price;

        public ResponInfo(String name, long price) {
            this.name = name;
            this.price = price;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public long getPrice() {
            return price;
        }

        public void setPrice(long price) {
            this.price = price;
        }
    }

具体来看requestCar() 方法:

public void requestCar() {

        ExecutorService executor = Executors.newFixedThreadPool(5);
        Future<ResponInfo> future = executor.submit(new Task());
    //在这里我们可以做其他的事情。
        try{
        //调用future.get()方法后,如果结果已经返回,那么将直接返回结果。
        //如果结果还没有返回,那么当前线程将会被阻塞,以等待结果的返回。
            ResponInfo info = future.get();
            Log.d(TAG,"result : "+info.getName());
        }catch (Exception e){

        }

    }

我们还可以加入超时时间:

public void requestCar() {

        ExecutorService executor = Executors.newFixedThreadPool(5);
        Future<ResponInfo> future = executor.submit(new Task());
        try{
        //2秒内不返回,将抛出异常。
            ResponInfo info = future.get(2, TimeUnit.SECONDS);
            Log.d(TAG,"result : "+info.getName());
        }catch (Exception e){
            e.printStackTrace();
        //取消任务
            future.cancel(true);
            Log.d(TAG, "cancel ");
        }

    }

五、在第一篇中说过,如何在子线程中再开启子线程请求网络数据。

其实在子线程中请求数据不难,但是如果我们在子线程中又开启了一个线程去请求数据呢?
例如当我们需要在子线程中,利用Volley 请求数据。我们知道Volley会另外开启子线程去连接网络。

如果发生这样的情况,我们需要让当前的线程阻塞,等待数据的返回,否则根本得不到数据的。

在第四步中Future.get()方法会阻塞当前的线程等待数据的返回,刚好符合我们的需求。

OK , 这篇就介绍到这里。
下一篇会介绍如何自定义Future,实现更灵活的功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值