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

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

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

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后送信到邮局
目录
相关文章
|
5天前
|
设计模式 安全 数据库连接
后端开发中的设计模式应用
在软件开发的浩瀚海洋中,设计模式如同灯塔,为后端开发者指引方向。它们不仅仅是代码的模板,更是解决复杂问题的智慧结晶。本文将深入探讨几种常见的设计模式,包括单例模式、工厂模式和观察者模式,并揭示它们在实际应用中如何提升代码的可维护性、扩展性和重用性。通过实例分析,我们将一窥这些模式如何在后端开发中大放异彩,助力构建高效、灵活的软件系统。
|
10天前
|
缓存 监控 Java
Java中的并发编程:理解并应用线程池
在Java的并发编程中,线程池是提高应用程序性能的关键工具。本文将深入探讨如何有效利用线程池来管理资源、提升效率和简化代码结构。我们将从基础概念出发,逐步介绍线程池的配置、使用场景以及最佳实践,帮助开发者更好地掌握并发编程的核心技巧。
|
7天前
|
设计模式 数据库连接 PHP
PHP中的设计模式应用与最佳实践
在本文中,我们将探讨PHP设计模式的应用和最佳实践。通过深入分析,揭示如何在实际项目中有效利用设计模式来优化代码结构、提升系统灵活性和维护性,并分享一些常见设计模式的实际应用案例。无论你是PHP初学者还是经验丰富的开发者,这篇文章都会对你有所帮助。
|
6天前
|
Java 调度 开发者
Java中的多线程基础及其应用
【9月更文挑战第13天】本文将深入探讨Java中的多线程概念,从基本理论到实际应用,带你一步步了解如何有效使用多线程来提升程序的性能。我们将通过实际代码示例,展示如何在Java中创建和管理线程,以及如何利用线程池优化资源管理。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和技巧,帮助你更好地理解和应用多线程编程。
|
16天前
|
存储 Java 程序员
优化Java多线程应用:是创建Thread对象直接调用start()方法?还是用个变量调用?
这篇文章探讨了Java中两种创建和启动线程的方法,并分析了它们的区别。作者建议直接调用 `Thread` 对象的 `start()` 方法,而非保持强引用,以避免内存泄漏、简化线程生命周期管理,并减少不必要的线程控制。文章详细解释了这种方法在使用 `ThreadLocal` 时的优势,并提供了代码示例。作者洛小豆,文章来源于稀土掘金。
|
25天前
|
设计模式 算法 开发者
深入理解工厂模式与策略模式:设计模式的灵活应用
深入理解工厂模式与策略模式:设计模式的灵活应用
|
3天前
|
设计模式 算法 PHP
PHP中的设计模式:策略模式的应用与实践
在软件开发中,设计模式是解决问题的最佳实践。本文将探讨PHP中的策略模式,通过实际应用案例,展示如何有效地使用策略模式来提高代码的灵活性和可维护性。我们将从基本概念入手,逐步深入到实际编码,最终实现一个具有策略模式的应用。
|
20天前
|
前端开发 C# 设计模式
“深度剖析WPF开发中的设计模式应用:以MVVM为核心,手把手教你重构代码结构,实现软件工程的最佳实践与高效协作”
【8月更文挑战第31天】设计模式是在软件工程中解决常见问题的成熟方案。在WPF开发中,合理应用如MVC、MVVM及工厂模式等能显著提升代码质量和可维护性。本文通过具体案例,详细解析了这些模式的实际应用,特别是MVVM模式如何通过分离UI逻辑与业务逻辑,实现视图与模型的松耦合,从而优化代码结构并提高开发效率。通过示例代码展示了从模型定义、视图模型管理到视图展示的全过程,帮助读者更好地理解并应用这些模式。
35 0
|
20天前
|
设计模式 安全 数据库连接
|
21天前
|
Java 程序员 调度
Java中的多线程基础与实战应用
【8月更文挑战第30天】在Java的世界里,多线程是提升程序性能的利器,但也是新手开发者常碰壁的难点。本文旨在通过浅显易懂的语言和生动的比喻,带领读者走进Java多线程的大门。我们将从线程的基本概念出发,逐步深入到线程的创建、启动、管理以及同步机制,最后通过一个简易版的图书管理系统实例,展示如何在实际开发中灵活运用多线程技术。