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

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

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

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后送信到邮局
目录
相关文章
|
2天前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
2天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
2天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是"将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。创建型模式分为5种:单例模式、工厂方法模式抽象工厂式、原型模式、建造者模式。
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
26天前
|
缓存 Java 开发者
Java多线程并发编程:同步机制与实践应用
本文深入探讨Java多线程中的同步机制,分析了多线程并发带来的数据不一致等问题,详细介绍了`synchronized`关键字、`ReentrantLock`显式锁及`ReentrantReadWriteLock`读写锁的应用,结合代码示例展示了如何有效解决竞态条件,提升程序性能与稳定性。
112 6
|
25天前
|
监控 Java 数据库连接
Java线程管理:守护线程与用户线程的区分与应用
在Java多线程编程中,线程可以分为守护线程(Daemon Thread)和用户线程(User Thread)。这两种线程在行为和用途上有着明显的区别,了解它们的差异对于编写高效、稳定的并发程序至关重要。
29 2
|
1月前
|
数据采集 存储 数据处理
Python中的多线程编程及其在数据处理中的应用
本文深入探讨了Python中多线程编程的概念、原理和实现方法,并详细介绍了其在数据处理领域的应用。通过对比单线程与多线程的性能差异,展示了多线程编程在提升程序运行效率方面的显著优势。文章还提供了实际案例,帮助读者更好地理解和掌握多线程编程技术。
|
1月前
|
存储 监控 安全
深入理解ThreadLocal:线程局部变量的机制与应用
在Java的多线程编程中,`ThreadLocal`变量提供了一种线程安全的解决方案,允许每个线程拥有自己的变量副本,从而避免了线程间的数据竞争。本文将深入探讨`ThreadLocal`的工作原理、使用方法以及在实际开发中的应用场景。
56 2
|
1月前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
58 6
|
1月前
|
设计模式 前端开发 JavaScript
JavaScript设计模式及其在实战中的应用,涵盖单例、工厂、观察者、装饰器和策略模式
本文深入探讨了JavaScript设计模式及其在实战中的应用,涵盖单例、工厂、观察者、装饰器和策略模式,结合电商网站案例,展示了设计模式如何提升代码的可维护性、扩展性和可读性,强调了其在前端开发中的重要性。
29 2
|
1月前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####