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

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

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

一、Callable :

public interface Callable {    /**     * 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 中有具有一下几个接口:

 Future submit(Callable task); Future 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 {    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 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);    }
//自定义Callablepublic 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 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 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,实现更灵活的功能。

更多相关文章

  1. Android基于OpenGL的GLSurfaceView创建一个Activity实现方法
  2. 【转】Android-Action Bar使用方法
  3. Android更新Ui线程的四个方法
  4. Android网络层与数据层设计
  5. Android获取本机IP地址(不是localhost)和MAC的方法
  6. Android 导入android源码有错,R.java文件不能自动生成解决方法
  7. Git,SVN使用方法杂记(更新中)

随机推荐

  1. PhoneGap对比html5写android应用程序【an
  2. 日记月累:Android SDK Manager无法更新
  3. android so文件是什么
  4. android 玩转ContentProvider之二--实现多
  5. android实现在横竖屏切换时页面信息不被
  6. android UI进阶之弹窗的使用
  7. 巨人的崛起 Android操作系统发展历程
  8. Android系统的Binder机制分析
  9. android TextView设置字过多长长度后面显
  10. Android编译环境搭建步骤(公司) - 写给自