Android 并发二三事之利用CountDownLatch 或 ConditionVariable实现自定义Future

来源:互联网 发布:网络诋毁公司如何处理 编辑:程序博客网 时间:2024/05/01 23:19

前言:

Android 并发第三篇
介绍如何利用 CountDownLatch 或 ConditionVariable 实现自定义Future,用于适应项目中的需求。
即阻塞当前线程,等待其他线程的结果返回,其功能类似于FutureTask。
首先介绍 CountDownLatch(共享锁 Java)以及 ConditionVariable(Android)。

一、CountDownLatch

1、CountDownLatch 简介:
CountDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行。

CountDownLatch是通过“共享锁”实现的。在创建CountDownLatch中时,会传递一个int类型参数count,该参数是“锁计数器”的初始状态,表示该“共享锁”最多能被count个线程同时获取。当某线程调用该CountDownLatch对象的await()方法时,该线程会等待“共享锁”可用时,才能获取“共享锁”进而继续运行。而“共享锁”可用的条件,就是“锁计数器”的值为0!而“锁计数器”的初始值为count,每当一个线程调用该CountDownLatch对象的countDown()方法时,才将“锁计数器”-1;通过这种方式,必须有count个线程调用countDown()之后,“锁计数器”才为0,而前面提到的等待线程才能继续运行!

CountDownLatch 本身是基于AQS实现的,具体其原理,这里不做太多介绍。

2、方法简介:

CountDownLatch(int count)
构造一个用给定计数初始化的 CountDownLatch。这个值只能被设置一次,而且CountDownLatch没有提供任何机制去重新设置这个计数值。

// 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。
void await()
// 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。
boolean await(long timeout, TimeUnit unit)
// 递减锁存器的计数,如果计数到达零,则释放所有等待的线程。
void countDown()
// 返回当前计数。
long getCount()

3、使用示例:

private void executeAfterWorker() {        try {            Log.d(TAG, "execute.....................");            CountDownLatch countDownLatch = new CountDownLatch(2);            Worker worker1 = new Worker(countDownLatch);            Worker worker2 = new Worker(countDownLatch);            worker1.start();            worker2.start();            Log.d(TAG, "execute wait");            //等待其他的线程执行完。            countDownLatch.await();            //其他线程执行完后,在执行其他的操作            Log.d(TAG, "execute finish......");        } catch (Exception e) {        }    }    private static class Worker extends Thread {        private CountDownLatch countDownLatch;        public Worker(CountDownLatch countDownLatch) {            this.countDownLatch = countDownLatch;        }        @Override        public void run() {            try {                Thread.sleep(100);                Log.d(TAG, Thread.currentThread().getId() + " run");            } catch (Exception e) {            } finally {                //调用countDown()计数器减一                countDownLatch.countDown();            }        }    }

4、结果:

 D/Demo: execute..................... D/Demo: execute wait D/Demo: 16090 run D/Demo: 16091 run D/Demo: execute finish......

5、利用CountDownLatch 自定义Future :

功能介绍:在子线程中开启其他线程联网请求数据,
阻塞当前线程,等待结果返回,或者超时。

//请求数据private void request() {        new Thread(new Runnable() {            @Override            public void run() {                try{                    ResultFuture future = new ResultFuture(1);                    request(future);                    com.loader.demo.ResponInfo responInfo= future.get();                    Log.d(TAG, responInfo.getName() + "requestAd");                }catch (Exception e){                }            }        }).start();    }    /**     * 模拟联网等耗时操作     * @param resultListener     */    private void request(final ResultListener resultListener) {        new Thread(new Runnable() {            @Override            public void run() {                try{                    Thread.sleep(3000);                }catch (Exception e){                }                //模拟成功后回调                resultListener.onSuccess(new com.loader.demo.ResponInfo("BMW", 2000));            }        }).start();    }
//数据实体    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;        }    }
/** * 自定义Future。 * 实现Future接口,提供get() 相关方法。 * 实现ResultListener 接口,获取成功或失败的回调信息。 * */public class ResultFuture implements Future<ResponInfo>, ResultListener{    private CountDownLatch mCountDownLatch;    private ResponInfo mResponInfo;    private boolean mResult = false;    public ResultFuture(int count) {        //开启count 个线程,等待 count 个线程执行完或超时,才会返回结果。        mCountDownLatch = new CountDownLatch(count);    }    @Override    public boolean cancel(boolean mayInterruptIfRunning) {        return false;    }    @Override    public boolean isCancelled() {        return false;    }    @Override    public boolean isDone() {        return false;    }    @Override    public ResponInfo get() throws InterruptedException, ExecutionException {        try {            return doGet(null);        }catch (Exception e){            return null;        }    }    @Override    public ResponInfo get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {        return doGet(TimeUnit.MILLISECONDS.convert(timeout, unit));    }    private ResponInfo doGet(Long time) throws InterruptedException, ExecutionException, TimeoutException{        //如果已经有结果,在直接返回        if(mResult) {            return mResponInfo;        }        //阻塞当前线程,直到调用mCountDownLatch.countDown();或者超时。        if(time == null) {            mCountDownLatch.await();        }else {//加入超时时间            mCountDownLatch.await(time, TimeUnit.MILLISECONDS);        }        if(!mResult) {            //超时抛出异常            throw new TimeoutException();        }        return mResponInfo;    }    @Override    public void onSuccess(ResponInfo responInfo) {        //成功,将结果赋值给mResponInfo,调用countDown()方法,计数器减一        this.mResponInfo = responInfo;        mResult = true;        mCountDownLatch.countDown();    }    @Override    public void onFail() {        //失败,计数器减一        mCountDownLatch.countDown();    }}
/** * 联网结果回调接口 */public interface ResultListener {    void onSuccess(ResponInfo responInfo);    void onFail();}

二、ConditionVariable:

1、ConditionVariable 简介:
ConditionVariable类位于android.os.ConditionVariable,它可以帮助Android线程同步。

其内部的实现的就是调用了wait()以及notifyAll();

之前介绍的 CountDownLatch 是Java 的类。而 ConditionVariable 是Android特有的。CountDownLatch 是共享锁,
当前线程阻塞,要等待其他的线程都执行完,也就是计数器等于0时,才会向下执行。

而ConditionVariable 是Android 对 wait() , notifyAll()的封装,调用block()方法阻塞当前线程,等待被唤醒。

2、构造方法:

    public ConditionVariable()    {        mCondition = false;    }    public ConditionVariable(boolean state)    {        mCondition = state;    }

默认mCondition 即为false。一般会用无参的构造方法。

3、方法介绍:
ConditionVariable为我们提供以下几个方法:
//释放所有被阻塞的线程,任何线程在调用open()后调用block()都不会生效,除非先调用了close() ,后调用block()。
public void open()
//将条件(condition)重置为关闭状态
public void close()
//阻塞当前线程,直到条件(condition)被打开。如果条件(condition)本来是打开的,将不会生效,会立即返回。
public void block()
//阻塞当前线程,直到条件(condition)被打开,或者超时。如果条件(condition)本来是打开的,将不会生效,会立即返回。
public boolean block(long timeout)

4、使用实例:

    private void executeAfterWorker() {        try {            Log.d(TAG, "execute.....................");            ConditionVariable conditionVariable = new ConditionVariable();            Worker worker = new Worker(conditionVariable);            worker.start();            Log.d(TAG, "execute wait");            conditionVariable.block();            Log.d(TAG, "execute finish......");        } catch (Exception e) {        }    }    private static class Worker extends Thread {        private ConditionVariable conditionVariable;        public Worker(ConditionVariable conditionVariable) {            this.conditionVariable = conditionVariable;        }        @Override        public void run() {            try {                Thread.sleep(100);                Log.d(TAG, Thread.currentThread().getId() + " run");            } catch (Exception e) {            } finally {                conditionVariable.open();            }        }    }

5、结果:

 D/Demo: execute..................... D/Demo: execute wait D/Demo: 16094 run D/Demo: execute finish......

6、下面来看如何利用ConditionVariable 打造自定义的Future。

在这里就给出Future 的实现类,其他的代码和 CountDownLatch 的都一样。

public class ResultFuture implements Future<ResponInfo>, ResultListener  {    private ResponInfo mResponInfo;    private boolean mResult = false;    private ConditionVariable conditionVariable;    public ResultFuture() {        conditionVariable = new ConditionVariable();    }    @Override    public boolean cancel(boolean mayInterruptIfRunning) {        return false;    }    @Override    public boolean isCancelled() {        return false;    }    @Override    public boolean isDone() {        return false;    }    @Override    public ResponInfo get() throws InterruptedException, ExecutionException {        try {            return doGet(null);        }catch (Exception e){            return null;        }    }    @Override    public ResponInfo get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {        return doGet(TimeUnit.MILLISECONDS.convert(timeout, unit));    }    private ResponInfo doGet(Long time) throws InterruptedException, ExecutionException, TimeoutException{        //如果已经有结果,在直接返回        if(mResult) {            return mResponInfo;        }        if(time == null) {            conditionVariable.block();        }else {//加入超时时间            conditionVariable.block(time);        }        if(!mResult) {            //超时抛出异常            throw new TimeoutException();        }        return mResponInfo;    }    @Override    public void onSuccess(ResponInfo responInfo) {        this.mResponInfo = responInfo;        mResult = true;        conditionVariable.open();    }    @Override    public void onFail() {        conditionVariable.open();    }}

OK, 利用 CountDownLatch 或 ConditionVariable 实现自定义的Future,就介绍到这里。其实原理非常的简单。就是需要当前的线程等待结果。
或者超时。

之所以介绍 CountDownLatch 和 ConditionVariable 是因为这两个比较熟悉。哈哈。在项目中都用到过多次。其中利用ConditionVariable 写过
和Volley想结合的代码。就是编写一个类实现Future,也同时实现Volley 访问网络成功和失败的结果。利用ConditionVariable 实现阻塞,等待结果。
感觉用起来非常的舒服。当然在 ResultFuture 编写的还相对简单,在其中我们还可以完善 isDone() 等方法。

下一篇开始将会介绍Android方面用于多线程方面的类,第一个是 AsyncTask , AsyncTask 的用法以及原理有太多人介绍过了,但这里我还是会介绍
其用法,原理,并尝试从不同的角度去分析,理解。

注: 文中如有有错误的,需要提高的代码,请您留言,我会及时改正。

0 0
原创粉丝点击