栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

并发设计模式——保护性暂停(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 的关注点还是在于临界值的是否满足条件。当达到设置的临界值是相关线程会被挂起。在很多的设计场景中都有这种设计模式的影子。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/991792.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号