怎么写条件等待?
即:当xxx条件满足时,唤醒某个独立线程执行某个任务。应该怎么写?
场景1.一等多。一个线程等待多个其他线程执行。
方案1.通用写法:每次执行完了check
func f2(){
new Thread({
doxx();
if check(){
cond.notifyAll();
}
});
}
方案2.如果是计数型条件:用CountDownLatch();
new Thread(()->{
state--;
latch.countDown();
});
new Thread(()->{
state--;
latch.countDown();
});
latch.await();
doTask();
方案3.不是计数型条件,但是可以把条件拆成多个函数,这些函数执行完了就满足条件,此时问题转化为计数型条件。
new Thread(()->{
//修改状态A
prepareA();
latch.countDown();
});
new Thread(()->{
//修改状态A和B
prepareAandB();
latch.countDown();
});
latch.await();
doTask();
场景2.大家一起等。多个线程都等待某个状态满足,满足后大家一起继续执行。
方案1.通用写法依然可行,类似于:
func f2(){
doxx();
if check(){
cond.notifyAll();
}else{
cond.await();
}
}
方案2.计数型条件可以用CyclicBarrier
问题:怎么实现复杂的非计数型条件等待?
设想这么一个场景:多个线程调用m个方法操作n个状态s1...sn,一个线程需要等待f(s1...sn)=0时启动执行task。怎么写?
如果用通用方法,每次改变n个状态中的任意一个都要显示的写if 检查then 通知 的代码,随着m增大或者方法内的代码复杂度变高,很容易出问题,读起来也恶心。
这种场景也没法转化为计数型条件。
方案1.扩展latch。
不晓得有没有现成的工具类,遇到这类问题可以自己封装一个工具类,提供类似于如下API
latch=new MyLatch(map,Checker::check(Map map),task)
...
new Thread(()->{
doXXX();
latch.change("s3",1);
});
可以看到,条件等待本质上是一个多线程发布/订阅模式(或观察者模式。两者有区别)怎么写的问题。这里的latch相当于发布/订阅模式中的broker。上文说的“一等多”是普通的发布/订阅或者观察者,而“大家一起等”可以理解成每个发布者既是发布者又是订阅者(分布式爬虫就会用到这种模式)。
方案2.劫持式写法
由每个方法显示的调用latch.change太丑了,可以劫持修改s1..sn状态的方法,改变状态时去通知broker。比如
S3=new S();
new Thread(()->{
doXXX();
S3.set(1)
});
class S{
void set(int i){
latch.change(name,i);
this.i=i;
}
}
方案3.观察者模式写法
网友评论