并发设计模式——保护性暂停(Guarded Suspension)

并发设计模式——保护性暂停(Guarded Suspension)

什么叫做 Guarded Suspension ?

guarded 的意思是”守护、担保“,suspension的意思是”悬挂、停止“,连起来的意思就是”受保护的暂停“。该模式常用在线程访问某个对象时,发现条件不满足,于是便暂时挂起等待条件满足再发起访问。
在这里插入图片描述

Guarded Suspension 的应用场景

在并发设计中,很多设计场景都有这种设计模式的影子,比如:

  • Java中的join、Future、FutureTask
  • 生产消费模式
  • Worker Thread设计模式
  • Java并发包中的 ArrayBlockingQueue

等等都使用了这种设计模式。

Guarded Suspension 源码展示
join()
public final synchronized void join(long millis)
throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (millis == 0) {
        while (isAlive()) {
            wait(0);
        }
    } else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

当调用无参join()方法时,就是形参millis = 0,从上面源码可以看出,当millis = 0时,调用线程开始了死等。等待正在执行的线程执行结束。

在Java源码中java.util.concurrent.ArrayBlockingQueue类中有两个方法是值得关注的:

put()
public void put(E e) throws InterruptedException {
    checkNotNull(e);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == items.length)
            notFull.await();
        enqueue(e);
    } finally {
        lock.unlock();
    }
}
take()
public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == 0)
            notEmpty.await();
        return dequeue();
    } finally {
        lock.unlock();
    }
}

当Queue容量达到上限时,一个线程调用put() 方法便陷入了无时限的等待,直到Queue容量不再满。

当Queue容量为0时,一个线程调用take() 方法也会陷入无时限的等待,直到Queue容量不为0。

Guarded Suspension 自定义实现

根据Java提供的源码,我们可以自定义设计出一个和 Guarded Suspension 模式有关的项目案例:

在一家餐馆里,来了一位客人点餐,客人点完餐后,厨师就开始做菜,在厨师做菜期间,服务员会根据餐盘中是否有菜判断菜是不是做好了,如果做好了,就去给客户送餐。

代码实现如下:

public class Test {
    public static void main(String[] args) throws InterruptedException {
        log.debug("客户开始点餐");
        //str1 str2 作为做菜的材料
        String str1 = "鱼香";
        String str2 = "肉丝";
        Object lock = new Object();
        //str3作为餐盘
        final String[] str3 = {null};
        new Thread(() -> {
            synchronized (lock) {
                log.debug("厨师开始做菜");
                try {
                    Thread.sleep(3000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.debug("厨师把菜做好了, 并装入了盘中");
                str3[0] = str1 + str2;
                lock.notify();
            }
        }, "厨师").start();

        Thread.sleep(200);

        new Thread(() -> {
            log.debug("服务员开始尝试获取鱼香肉丝");
            //如果餐盘为空,则表示没有做好
            if (str3[0] == null) {
                log.debug("厨师还没做好,获取失败");
                log.debug("开始等待厨师做菜");
                synchronized (lock) {
                }
            }
            log.debug("服务员成功获取到了菜");
        }, "服务员").start();
    }
}

在这里插入图片描述

根据代码的运行结果可以看出,厨师开始做菜后,服务员尝试获取,但发现条件不满足后,便开始了无时限的等待,直到厨师做好了菜,这就是典型的保护性暂停案例。

除了一对一以外,还有一对多,多对多的情况,线程池便是运用了多对多的情况。

并且针对以上代码,我们还可以根据情况给出相应的优化,比如有时限的等待,如果一段时间内未获取到满足的条件,便放弃本次获取等等。

总结

相对来说Guarded Suspension设计模式并不是一个复杂的模式而是比较简单的一个模式,从上面例子中可以看出使用也相对比较简单。Guarded Suspension 的关注点还是在于临界值的是否满足条件。当达到设置的临界值是相关线程会被挂起。在很多的设计场景中都有这种设计模式的影子。

相对来说Guarded Suspension设计模式并不是一个复杂的模式而是比较简单的一个模式,从上面例子中可以看出使用也相对比较简单。Guarded Suspension 的关注点还是在于临界值的是否满足条件。当达到设置的临界值是相关线程会被挂起。在很多的设计场景中都有这种设计模式的影子。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值