【多线程:设计模式】保护性暂停的应用与扩展

简介: 【多线程:设计模式】保护性暂停的应用与扩展

【多线程:设计模式】保护性暂停的应用与扩展

01.join实现原理

join源码

我们来分析一下它的源码

我们可以看出如果join给定的时间小于0就抛出异常

如果给定的时间为0,其实也就是不加参数的join,他就会判断是否isAlive也就是判断调用join的线程是否存活,如果存活则wait(0)即一直等待,注意这里的wait是让t1.join同步的线程等待 t1线程运行。举一个例子就是在main线程运行t1.join(),那么isAlive判断的是t1线程是否存在,wait(0)是让main线程进行等待

如果给定的时间大于0,那么这里的代码就和我们的上一篇内容里的保护性暂停扩展-增加超时 一模一样了,这里就是 保护性暂停 这个设计模式的体现

join方法整体就是 保护性暂停 设计模式 的体现,只不过它没有传递数据 只是用来让其他线程陷入等待 使其调用join的线程可以先执行

02.扩展-解耦等待与成产

介绍

这个图中 t0 t2 t4是寄信人线程,t1 t3 t5负责邮递员线程 负责送信与代写信。每个信都有自己的id与内容 id可以理解为收信人的地址,寄信人通过邮递员把新送给收信人,这里我们只研究寄信人与邮递员之间的消息传递。注意:一个邮递员只能寄一封信。
为什么要这样设计
这样设计的好处是解耦 也就是降低消息传递的复杂度与关联度,我们把Futures当做邮局 寄信人 把信给邮局 然后邮递员来取信送出,我们可以想象一下 如果没有这个邮局 我们会怎么样?我们需要亲自跑到邮递员家里 把信给他 这期间邮递员可能有事出门了 你就白去了 总之这样做 效率很低,这就是邮局的好处。

代码

@Slf4j(topic = "c.Test20")
public class Test20 {
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 3; i++) {
            new People().start();
        }
        Sleeper.sleep(1);
        for (Integer id : Mailboxes.getIds()) {
            new Postman(id, "内容" + id).start();
        }
    }
}

@Slf4j(topic = "c.People")
class People extends Thread{
    @Override
    public void run() {
        // 收信
        GuardedObject guardedObject = Mailboxes.createGuardedObject();
        log.debug("开始寄信 id:{}", guardedObject.getId());
        Object mail = guardedObject.get(5000);
        log.debug("寄信的内容 id:{}, 内容:{}", guardedObject.getId(), mail);
    }
}

@Slf4j(topic = "c.Postman")
class Postman extends Thread {
    private int id;
    private String mail;

    public Postman(int id, String mail) {
        this.id = id;
        this.mail = mail;
    }

    @Override
    public void run() {
        GuardedObject guardedObject = Mailboxes.getGuardedObject(id);
        log.debug("送信 id:{}, 内容:{}", id, mail);
        guardedObject.complete(mail);
    }
}

class Mailboxes {
    private static Map<Integer, GuardedObject> boxes = new Hashtable<>();

    private static int id = 1;
    // 产生唯一 id
    private static synchronized int generateId() {
        return id++;
    }

    public static GuardedObject getGuardedObject(int id) {
        return boxes.remove(id);
    }

    public static GuardedObject createGuardedObject() {
        GuardedObject go = new GuardedObject(generateId());
        boxes.put(go.getId(), go);
        return go;
    }

    public static Set<Integer> getIds() {
        return boxes.keySet();
    }
}

// 增加超时效果
class GuardedObject {

    // 标识 Guarded Object
    private int id;

    public GuardedObject(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    // 结果
    private Object response;

    // 获取结果
    // timeout 表示要等待多久 2000
    public Object get(long timeout) {
        synchronized (this) {
            // 开始时间 15:00:00
            long begin = System.currentTimeMillis();
            // 经历的时间
            long passedTime = 0;
            while (response == null) {
                // 这一轮循环应该等待的时间
                long waitTime = timeout - passedTime;
                // 经历的时间超过了最大等待时间时,退出循环
                if (timeout - passedTime <= 0) {
                    break;
                }
                try {
                    this.wait(waitTime); // 虚假唤醒 15:00:01
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 求得经历时间
                passedTime = System.currentTimeMillis() - begin; // 15:00:02  1s
            }
            return response;
        }
    }

    // 产生结果
    public void complete(Object response) {
        synchronized (this) {
            // 给结果成员变量赋值
            this.response = response;
            this.notifyAll();
        }
    }
}

结果

12:21:51.157 c.People [Thread-1] - 开始寄信 id:3
12:21:51.158 c.People [Thread-0] - 开始寄信 id:1
12:21:51.157 c.People [Thread-2] - 开始寄信 id:2
12:21:52.171 c.Postman [Thread-4] - 送信 id:2, 内容:内容2
12:21:52.171 c.Postman [Thread-5] - 送信 id:1, 内容:内容1
12:21:52.171 c.Postman [Thread-3] - 送信 id:3, 内容:内容3
12:21:52.171 c.People [Thread-0] - 寄信的内容 id:1, 内容:内容1
12:21:52.171 c.People [Thread-2] - 寄信的内容 id:2, 内容:内容2
12:21:52.171 c.People [Thread-1] - 寄信的内容 id:3, 内容:内容3

对上述结果进行分析与解释

解释

我们可以看出 我们寄信后每个信 都有一个对应的 邮递员来送信 信的内容与id也一致

分析

我们首先关注到了几个类分别是:
GuardedObject

GuardedObject 类是写入消息与获取消息的类,由于信是邮递员代写 所以邮递员写信就是写入消息 收信人得到信就是获取消息
这个类有几个方法
getId:获取这个信的id
get:获取这个信,有时限的等待,如果获取时间过长就放弃获取
complete:写信

Mailboxes

Mailboxes类 可以理解为邮局 负责解耦寄信人与邮递员,主要形式是通过id与信之间的一一对应实现,邮递员不需要知道寄信的是谁 他只需要关注他要寄到的地址(就是信的id)是什么。
这个类有几个方法与成员变量
boxes变量:是一个Hashtable类型 因为Hashtable是线程安全的 所以不用考虑关于 用到有关Hashtable的方法的线程安全问题,Hashtable的key是id value是信,我们通过id找到信
generateId方法:作用是生产信的id,在createGuardedObject创建信时被调用
getGuardedObject方法:作用是邮递员通过id获取信 然后邮递员送信 因为已经送信了 所以我们需要把它的Hashtable删除,所以我们这里选择使用的方法是boxes.remove(id); 通过remove获取信 并且删除它的Hashtable
createGuardedObject方法:作用是创建一封信 也就是一个Hashtable,可以理解为寄信人 要寄信 需要先创建一个信封并且在信封上写上地址(id)
getIds方法:这个方法的作用是获取所有id,目的是 让邮递员选择一个id对应的信送出

Postman

Postman线程类 是邮递员类,作用是送信和 代写信
这个类有几个方法与成员变量
id变量:要送信的id
mail变量:要送信的内容
Postman:有参构造,参数为id mail,通过id获取到信(内容为空) 然后往其中写入内容mail
run方法:作用为每个线程 都在run方法 中 通过id获取信 并且写入内容mail

People

People线程类 是寄信人类,作用是 创建信
这个类的方法:
run方法:作用是创建信,并 在一段时间后 获取信的内容,如果5s后仍然没有获取到则放弃获取 说明信的内容写入超时

Test20

Test20类是入口类
创建了3个People线程与3个Postman线程,并且在People线程运行1s后 Postman线程开始运行,模拟1s后送信到邮局
目录
相关文章
|
6小时前
|
设计模式 前端开发 JavaScript
【JavaScript 技术专栏】JavaScript 设计模式与实战应用
【4月更文挑战第30天】本文探讨JavaScript设计模式在提升开发效率和代码质量中的关键作用。涵盖单例、工厂、观察者、装饰器和策略模式,并通过实例阐述其在全局状态管理、复杂对象创建、实时数据更新、功能扩展和算法切换的应用。理解并运用这些模式能帮助开发者应对复杂项目,提升前端开发能力。
|
6小时前
|
设计模式 JavaScript 算法
js设计模式-策略模式与代理模式的应用
策略模式和代理模式是JavaScript常用设计模式。策略模式通过封装一系列算法,使它们可互换,让算法独立于客户端,提供灵活的选择。例如,定义不同计算策略并用Context类执行。代理模式则为对象提供代理以控制访问,常用于延迟加载或权限控制。如创建RealSubject和Proxy类,Proxy在调用RealSubject方法前可执行额外操作。这两种模式在复杂业务逻辑中发挥重要作用,根据需求选择合适模式解决问题。
|
6小时前
|
设计模式 算法 Java
设计模式在Java开发中的应用
设计模式在Java开发中的应用
18 0
|
6小时前
|
设计模式 存储 前端开发
18:JavaBean简介及其在表单处理与DAO设计模式中的应用-Java Web
18:JavaBean简介及其在表单处理与DAO设计模式中的应用-Java Web
26 4
|
6小时前
|
设计模式 安全 Java
|
6小时前
|
设计模式 消息中间件 Java
Java 设计模式:探索发布-订阅模式的原理与应用
【4月更文挑战第27天】发布-订阅模式是一种消息传递范式,被广泛用于构建松散耦合的系统。在 Java 中,这种模式允许多个对象监听和响应感兴趣的事件。
37 2
|
6小时前
|
设计模式 算法 Java
Java 设计模式:深入模板方法模式的原理与应用
【4月更文挑战第27天】模板方法模式是一种行为设计模式,主要用于定义一个操作中的算法的框架,允许子类在不改变算法结构的情况下重定义算法的某些特定步骤。
22 1
|
6小时前
|
设计模式 算法 Java
Java 设计模式:探索策略模式的概念和实战应用
【4月更文挑战第27天】策略模式是一种行为设计模式,它允许在运行时选择算法的行为。在 Java 中,策略模式通过定义一系列的算法,并将每一个算法封装起来,并使它们可以互换,这样算法的变化不会影响到使用算法的客户。
23 1
|
6小时前
|
设计模式 存储 安全
Java 设计模式:深入单例模式的理解与应用
【4月更文挑战第27天】单例模式是一种常用的设计模式,在 Java 开发中扮演着重要角色。此模式的主要目的是保证一个类只有一个实例,并提供一个全局访问点。
20 0
|
6小时前
|
设计模式 Java
Java 设计模式:工厂模式与抽象工厂模式的解析与应用
【4月更文挑战第27天】设计模式是软件开发中用于解决常见问题的典型解决方案。在 Java 中,工厂模式和抽象工厂模式是创建型模式中非常核心的模式,它们主要用于对象的创建,有助于增加程序的灵活性和扩展性。本博客将详细介绍这两种模式的概念、区别以及如何在实际项目中应用这些模式。
17 1