美文网首页
常用并发编程工具类的使用笔记

常用并发编程工具类的使用笔记

作者: 周群力 | 来源:发表于2019-10-17 20:48 被阅读0次

怎么写条件等待?

即:当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.观察者模式写法

相关文章

网友评论

      本文标题:常用并发编程工具类的使用笔记

      本文链接:https://www.haomeiwen.com/subject/ifavmctx.html