并发设计模式(二)

文章导读:并发编程高级篇
(一)并发设计模式

多线程版本的 If

Guarded Suspension模式

Guarded Suspension模式也被叫做Guarded Wait 模式,我也更倾向这种叫法。实际上,本质就是是一种等待唤醒机制的实现。
我觉得这个模式比较经典的一个应用是异步转同步(比如Dubbo 中 DefaultFuture 这个类也是采用的这种方式)

又比如用户通过浏览器发过来一个请求,而服务调用方A接受到这个请求,而这个请求需要请求其它服务提供方B的某个接口来返回结果。然而这个服务B暴露出来的交互方式并不是Http的方式,而是通过MQ。所以服务调用方A会将这个请求转换成一个异步消息发送给 MQ,等 MQ 返回结果后,再将这个结果返回至浏览器。那服务A在发送MQ之后,如何等待返回结果呢?这些场景都可以通过Guarded Suspension模式来解决。

下图就是 Guarded Suspension 模式的结构图,非常简单,一个对象 GuardedObject,内部有一个成员变量——受保护的对象,以及两个成员方法——get(Predicate p)和onChanged(T obj)方法
630f3eda98a0e6a436953153c68464dc

GuardedObject 的内部实现非常简单,是管程的一个经典用法,你可以参考下面的示例代码,核心是:get() 方法通过条件变量的 await() 方法实现等待,onChanged() 方法通过条件变量的 signalAll() 方法实现唤醒功能。逻辑还是很简单的,所以这里就不再详细介绍了。


class GuardedObject<T>{
  //受保护的对象
  T obj;
  final Lock lock = 
    new ReentrantLock();
  final Condition done =
    lock.newCondition();
  final int timeout=1;
  //获取受保护对象  
  T get(Predicate<T> p) {
    lock.lock();
    try {
      //MESA管程推荐写法
      while(!p.test(obj)){
        done.await(timeout, 
          TimeUnit.SECONDS);
      }
    }catch(InterruptedException e){
      throw new RuntimeException(e);
    }finally{
      lock.unlock();
    }
    //返回非空的受保护对象
    return obj;
  }
  //事件通知方法
  void onChanged(T obj) {
    lock.lock();
    try {
      this.obj = obj;
      done.signalAll();
    } finally {
      lock.unlock();
    }
  }
}

看代码的话,这不就是管程的等待唤醒机制吗。是的,只不过 Guarded Suspension 模式将其规范化了

怎么理解他是属于多线程版本的If呢?

在单线程中,if语句是不会使用等待的。因为在只有一个线程的条件下,如果这个线程被阻塞,那就没有其他活动线程了,这意味着 if 判断条件的结果也不会发生变化了。但是多线程场景中,等待就变得有意义了,这种场景下,if 判断条件的结果是可能发生变化的。所以,用“多线程版本的 if”来理解这个模式会更简单。

CompletableFuture.get(好像也是通过这种方式实现的)。我理解受保护对象就是返回结果。返回结果为空就等待,直到被唤醒

Balking模式

多线程版本的If中,Guarded Suspension模式是需要等待的,而且还很执着,必须要等到条件为真。但很显然这个世界,不是所有场景都需要这么执着,有时候我们还需要快速放弃。Balking模式也可以理解是多线程版本的If,Balking 模式是不会等待,条件不符合,直接就Pass了。

比如编辑器自动保存或者自动保存路由表功能等,这两个功能一般都会使用一个定时任务啥的去做,每隔几秒中,检查共享变量 changed是否发生改变…

Balking 模式有一个非常典型的应用场景就是单次初始化


class InitTest{
  boolean inited = false;
  synchronized void init(){
    // 竞态条件
    if(inited){
      return;
    }
    //省略doInit的实现
    doInit();
    inited=true;
  }
}

线程安全的单例模式本质上其实也是单次初始化,所以可以用 Balking 模式来实现线程安全的单例模式(懒汉式和双重检查模式)

两个区别

Balking 模式和 Guarded Suspension 模式从实现上看似乎没有多大的关系,Balking 模式只需要用互斥锁就能解决,而 Guarded Suspension 模式则要用到管程这种高级的并发原语;但是从应用的角度来看,它们解决的都是“线程安全的 if”语义,不同之处在于,Guarded Suspension 模式会等待 if 条件为真,而 Balking 模式不会等待。

Balking 模式的经典实现是使用互斥锁,你可以使用 Java 语言内置 synchronized,也可以使用 SDK 提供 Lock;如果你对互斥锁的性能不满意,可以尝试采用 volatile 方案,不过使用 volatile 方案需要你更加谨慎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值