前言:
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 的用法以及原理有太多人介绍过了,但这里我还是会介绍
其用法,原理,并尝试从不同的角度去分析,理解。
注: 文中如有有错误的,需要提高的代码,请您留言,我会及时改正。