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

前言:

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值