CountDownLatch
用于一个线程等待多个其他线程任务完成后,再继续执行。这个还是比较有用的,比如初始化分多个模块,而多个模块里面又自己创建了子线程来初始化。
又比如在实际项目中查询天气并播放功能:在子线程查询天气数据的同时(标记为任务1),为了用户体验会同时另外播放一句固定的话,如“正在为您查询天气”(标记为任务2),我们必须要等到这两个任务同时完成后,才能根据任务2返回的天气数据再进行后面的天气数据播放。当然实现这种功能的办法有很多,最傻瓜式的办法自己封装了一个通知的工具:
'public class WaitHandler<T> implements Handler.Callback{
private Handler handler;
private T value;
private static final int CHECK=0x001;
private static final int SUC=0x002;
private static final int DELAY=200;
private volatile CallBack callBack;
public WaitHandler(CallBack callBack) {
this.callBack = callBack;
handler = new Handler(Looper.getMainLooper(),this);
}
public void waitValue(){
if (handler!=null)
handler.sendEmptyMessageDelayed(CHECK,DELAY);
}
public void setValue(T t){
Message.obtain(handler,SUC,t).sendToTarget();
}
@Override
public boolean handleMessage(Message msg) {
switch (msg.what){
case CHECK:
if (value==null){
if (handler!=null)
handler.sendEmptyMessageDelayed(CHECK,DELAY);
}else if (callBack!=null){
callBack.onResult(value);
close();
}
break;
case SUC:
value = (T) msg.obj;
}
return false;
}
public void close(){
callBack=null;
if (handler!=null){
handler.removeCallbacksAndMessages(null);
}
handler=null;
}
public interface CallBack<T> {
void onResult(T t);
}
}'
因为从用户体验上,任务1的提示语播放不管什么情况都需要播放完成,所以在任务1播放完成的地方直接调用WaitHandler的waitValue方法等待任务2的执行,在任务2完成的地方用setValue方法设置进天气数据。这样也可以完成效果,但稍显麻烦。
这种情况如果用CountDownLatch,就可以定义初始值为2,主任务线程启动提属于播放和天气数据获取后调用await等待。播放完成和天气数据分别获取完成后执行countDown,当计数为0时候则可以在主任务线程继续执行,从思路上来说,直接使用这种线程协作的工具要比自己构造通知机制要简单一些
CyclicBarrier
这个呢,中文翻译叫栅栏。表面上实现的功能和CountDownLatch很像。不过这个的方法实际的含义和CountDownLatch的含义不同,如,await方法,都是等待,CountDownLatch是一个线程等待其他的线程,一般只由一个线程调用,而CyclicBarrier中一般来说,有几个并行的线程,就会有几处调用await,在CyclicBarrier中,await代表自己达到栅栏了,当所有任务都调用了此方法,代表都到达栅栏了,那么等待结束,在CyclicBarrier的构造CyclicBarrier(int parties, Runnable barrierAction)中,Runnable参数是所有任务达到栅栏后,最后到达的那个线程去执行,相当于后面的继续操作,从上面的例子来看的话,这里就是把任务2拿到的天气数据去播放。
总之,CountDownLatch和CyclicBarrier的区别有点像下面这种关系:
![](https://img.haomeiwen.com/i1516518/5ea77db4f7fdaf1b.png)
![](https://img.haomeiwen.com/i1516518/24e712c93f34cf37.png)
Semaphore
这个其实就是常说的信号量,听起来很高级,但如果用起来的话还是很有用的很方便的。
主要两个方法,acquire和release。用来控制线程对资源的访问。比如某个资源要被某个线程访问,那么线程需要先调用acquire来获取,如果成功则执行后面的操作,完成操作后调用release来释放,以方便其他线程来使用。当访问量达到初始设置的上线个数时候,acquire方法会被阻塞,直到获取成功。
换句话说,这个访问上线如果设置为1,那么相当于互斥锁,如果大于1,用于控制有限资源的访问个数。
Exchanger
这个用于线程的交换数据,后续再说。
当遇到多线程需要较复杂的协作的时候,先 不急于自定义各种消息机制的操作,不妨先想一下这几个工具,很可能会方便很多,达到事半功倍的效果
网友评论