0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

异步非阻塞框架是如何实现的

科技绿洲 来源:Java威廉希尔官方网站 指北 作者:Java威廉希尔官方网站 指北 2023-10-09 10:29 次阅读

1、什么是观察者模式?

Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

观察者模式(Observer Design Pattern):在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会得到通知并自动更新。

说人话:也叫发布订阅模式,能够很好的解耦一个对象改变,自动改变另一个对象这种情况。

2、观察者模式定义

图片

①、Subject 被观察者

定义被观察者必须实现的职责, 它必须能够动态地增加、 取消观察者。它一般是抽象类或者是实现类, 仅仅完成作为被观察者必须实现的职责:管理观察者并通知观察者。

②、Observer观察者

观察者接收到消息后, 即进行update(更新方法) 操作, 对接收到的信息进行处理。

③、ConcreteSubject具体的被观察者

定义被观察者自己的业务逻辑, 同时定义对哪些事件进行通知。

④、ConcreteObserver具体的观察者

每个观察在接收到消息后的处理反应是不同, 各个观察者有自己的处理逻辑。

3、观察者模式通用代码

/**
 * 观察者
 */
public interface Observer {
    // 更新方法
    void update();
}
/**
 * 具体观察者
 */
public class ConcreteObserver implements Observer{
    @Override
    public void update() {
        System.out.println("接受到信息,并进行处理");
    }
}
/**
 * 被观察者
 */
public abstract class Subject {
    // 定义一个被观察者数组
    private List< Observer > obsList = new ArrayList<  >();

    // 增加一个观察者
    public void addObserver(Observer observer){
        obsList.add(observer);
    }

    // 删除一个观察者
    public void delObserver(Observer observer){
        obsList.remove(observer);
    }

    // 通知所有观察者
    public void notifyObservers(){
        for (Observer observer : obsList){
            observer.update();
        }
    }
}
/**
 * 具体被观察者
 */
public class ConcreteSubject extends Subject{
    // 具体的业务
    public void doSomething(){
        super.notifyObservers();
    }
}
public class ObserverClient {

    public static void main(String[] args) {
        // 创建一个被观察者
        ConcreteSubject subject = new ConcreteSubject();
        // 定义一个观察者
        Observer observer = new ConcreteObserver();
        // 观察者观察被观察者
        subject.addObserver(observer);
        subject.doSomething();
    }
}

4、JDK 实现

在 JDK 的 java.util 包下,已经为我们提供了观察者模式的抽象实现,感兴趣的可以看看,内部逻辑其实和我们上面介绍的差不多。

观察者 java.util.Observer

图片

被观察者 java.util.Observable

图片

图片

5、实例

用户进行注册,注册完成之后,会发一封欢迎邮件。

5.1 普通实现

图片

public class UserController {

    public void register(String userName, String passWord){
        // 1、根据用户名密码保存在数据库
        Long userId = saveUser(userName, passWord);
        // 2、如果上一步有结果则发送一封欢迎邮件
        if(userId != null){
            Mail.sendEmail(userId);
        }
    }


    public Long saveUser(String userName, String passWord){
        return 1L;
    }
}

上面的注册接口实现了两件事,注册和发送邮件,很明显违反了单一职责原则,但假设这个注册需求是不是经常变动的,这样写也没有什么问题,但是假如需求变动,比如不仅要发送邮件,还得发送短信,那还这样写,那register接口会变得很复杂。

那应该如何简化呢?没错,就是观察者模式。

图片

5.2 观察者模式实现

我们直接套用 JDK 的实现。

import java.util.Observable;

/**
 * 用户登录——被观察者
 */
public class UserControllerObservable extends Observable {

    public void register(String userName, String passWord){
        // 1、根据用户名密码保存在数据库
        Long userId = saveUser(userName, passWord);
        // 2、如果上一步有结果则通知所有观察者
        if(userId != null){
            super.setChanged();
            super.notifyObservers(userName);
        }
    }

    public Long saveUser(String userName, String passWord){
        return 1L;
    }

}
import java.util.Observable;
import java.util.Observer;

/**
 * 发送邮件——观察者
 */
public class MailObserver implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("发送邮件:" + arg + "欢迎你");
    }
}
/**
 * 发送手机短信——观察者
 */
public class SMSObserver implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("发送短信:" + arg + "欢迎你");
    }
}

测试:

public class UserClient {
    public static void main(String[] args) {
        UserControllerObservable observable = new UserControllerObservable();
        observable.addObserver(new MailObserver());
        observable.addObserver(new SMSObserver());
        observable.register("张三","123");
    }
}

通过观察者模式改写后,后面用户注册,就算在增加别的操作,我们也只需要增加一个观察者即可,而注册接口 register 不会有任何改动。

5.3 异步模式优化

在回到前面那张图:

图片

注册之后进行的两步操作:发送邮件和发送短信,上面我们通过观察者模式改写之后,虽然流程很清晰,但是我们发现是顺序执行的,但其实这两步操作没有先后顺序,于是,我们可以改成异步模式,增加执行效率。

/**
 * 发送邮件——观察者
 */
public class MailObserver implements Observer {
    
    private Executor executor = Executors.newFixedThreadPool(2);

    @Override
    public void update(Observable o, Object arg) {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("发送邮件:" + arg + "欢迎你");
            }
        });

    }
}

5、EventBus

翻译为“事件总线”,它提供了实现观察者模式的骨架代码。我们可以基于此框架,非常容易地在自己的业务场景中实现观察者模式,不需要从零开始开发。其中,Google Guava EventBus 就是一个比较著名的 EventBus 框架,它不仅仅支持异步非阻塞模式,同时也支持同步阻塞模式。

PS:Google Guava 是一个特别好用的工具包,里面的代码也都实现的比较优雅,大家感兴趣的可以研究研究源码。

https://github.com/google/guava

下面我们以上面的例子来说明如何使用 EventBus:

①、导如 Guava 包

< dependency >
    < groupId >com.google.guava< /groupId >
    < artifactId >guava< /artifactId >
    < version >30.1.1-jre< /version >
< /dependency >

②、具体代码如下:

import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.EventBus;

import java.util.List;
import java.util.concurrent.Executors;

public class UserController {
    private EventBus eventBus;

    public UserController(){
        eventBus = new AsyncEventBus(Executors.newFixedThreadPool(2));
    }

    /**
     * 注意:泛型参数是 Object,而不是接口 Observer
     * @param observerList
     */
    public void setObserverList(List< Object > observerList){
        for(Object observer : observerList){
            eventBus.register(observer);
        }
    }

    public void register(String userName, String passWord){
        // 1、根据用户名密码保存在数据库
        Long userId = saveUser(userName, passWord);
        // 2、如果上一步有结果则通知所有观察者
        if(userId != null){
            eventBus.post(userName);
        }
    }


    public Long saveUser(String userName, String passWord){
        return 1L;
    }
}
import com.google.common.eventbus.Subscribe;

/**
 * 发送邮件——观察者
 */
public class MailObserver{

    @Subscribe
    public void sendMail(String userName) {
        System.out.println("发送邮件:" + userName + "欢迎你");
    }
}
import com.google.common.eventbus.Subscribe;

/**
 * 发送手机短信——观察者
 */
public class SMSObserver{

    @Subscribe
    public void sendSMS(String userName) {
        System.out.println("发送短信:" + userName + "欢迎你");
    }
}

测试:

public class EventBusClient {
    public static void main(String[] args) {
        UserController userController = new UserController();
        List< Object > observerList = new ArrayList<  >();
        observerList.add(new MailObserver());
        observerList.add(new SMSObserver());
        userController.setObserverList(observerList);
        userController.register("张三","123");
    }
}

利用 EventBus 框架实现的观察者模式,跟从零开始编写的观察者模式相比,从大的流程上来说,实现思路大致一样,都需要定义 Observer,并且通过 register() 函数注册 Observer,也都需要通过调用某个函数(比如,EventBus 中的 post() 函数)来给 Observer 发送消息(在 EventBus 中消息被称作事件 event)。但在实现细节方面,它们又有些区别。基于 EventBus,我们不需要定义 Observer 接口,任意类型的对象都可以注册到 EventBus 中,通过 @Subscribe 注解来标明类中哪个函数可以接收被观察者发送的消息。

6、观察者模式优点

①、观察者和被观察者之间是抽象耦合

不管是增加观察者还是被观察者都非常容易扩展,在系统扩展方面会得心应手。

②、建立一套触发机制

被观察者变化引起观察者自动变化。但是需要注意的是,一个被观察者,多个观察者,Java的消息通知默认是顺序执行的,如果一个观察者卡住,会导致整个流程卡住,这就是同步阻塞。

所以实际开发中没有先后顺序的考虑使用异步,异步非阻塞除了能够实现代码解耦,还能充分利用硬件资源,提高代码的执行效率。

另外还有进程间的观察者模式,通常基于消息队列来实现,用于实现不同进程间的观察者和被观察者之间的交互。

7、观察者模式应用场景

①、关联行为场景。

②、事件多级触发场景。

③、跨系统的消息交换场景, 如消息队列的处理机制。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 接口
    +关注

    关注

    33

    文章

    8605

    浏览量

    151181
  • 数据库
    +关注

    关注

    7

    文章

    3800

    浏览量

    64396
  • 代码
    +关注

    关注

    30

    文章

    4788

    浏览量

    68619
  • JDK
    JDK
    +关注

    关注

    0

    文章

    81

    浏览量

    16596
收藏 人收藏

    评论

    相关推荐

    Verilog语言中阻塞阻塞赋值的不同

    来源:《Verilog数字系统设计(夏宇闻)》 阻塞阻塞赋值的语言结构是Verilog 语言中最难理解概念之一。甚至有些很有经验的Verilog 设计工程师也不能完全正确地理解:何时使用
    的头像 发表于 08-17 16:18 6384次阅读

    Verilog阻塞阻塞原理分析

    Verilog阻塞阻塞原理分析在Verilog语言最难弄明白的结构中“阻塞赋值”要算一个。甚至是一些很有经验的工程师也不完全明白“
    发表于 11-23 12:02

    同步与异步阻塞阻塞的区别是什么

    同步与异步阻塞阻塞的区别
    发表于 01-26 06:12

    怎么使用Select实现阻塞网络编程?

    使用Select实现阻塞网络编程
    发表于 03-30 07:34

    Java阻塞通信研究

    本文针对Java NIO 的特性做出分析与阐述,对网络应用中阻塞通信与阻塞通信、NIO的阻塞工作机制以及网络通信中非
    发表于 08-10 10:15 18次下载

    verilog中阻塞赋值和阻塞赋值

    阻塞阻塞语句作为verilog HDL语言的最大难点之一,一直困扰着FPGA设计者,即使是一个颇富经验的设计工程师,也很容易在这个点上犯下一些不必要的错误。阻塞
    发表于 03-15 10:57 7001次阅读

    深入理解阻塞阻塞赋值

    这是一个很好的学习阻塞阻塞的资料,对于FPGA的学习有很大帮助。
    发表于 04-22 11:00 11次下载

    简述阻塞赋值和阻塞赋值的可综合性

    阻塞赋值和阻塞赋值的可综合性 Blocking Assignment阻塞赋值和NonBlocking Assignment
    的头像 发表于 05-12 09:45 2727次阅读
    简述<b class='flag-5'>阻塞</b>赋值和<b class='flag-5'>非</b><b class='flag-5'>阻塞</b>赋值的可综合性

    简述Verilog HDL中阻塞语句和阻塞语句的区别

      在Verilog中有两种类型的赋值语句:阻塞赋值语句(“=”)和阻塞赋值语句(“=”)。正确地使用这两种赋值语句对于Verilog的设计和仿真非常重要。 Verilog语言中讲的阻塞
    的头像 发表于 12-02 18:24 6223次阅读
    简述Verilog HDL中<b class='flag-5'>阻塞</b>语句和<b class='flag-5'>非</b><b class='flag-5'>阻塞</b>语句的区别

    时序逻辑中的阻塞阻塞

    Verilog HDL的赋值语句分为阻塞赋值和阻塞赋值两种。阻塞赋值是指在当前赋值完成前阻塞其他类型的赋值任务,
    的头像 发表于 03-15 13:53 3061次阅读

    一文了解阻塞赋值与阻塞赋值

    今天给大家普及一下阻塞赋值和阻塞赋值的相关知识
    的头像 发表于 07-07 14:15 2189次阅读
    一文了解<b class='flag-5'>阻塞</b>赋值与<b class='flag-5'>非</b><b class='flag-5'>阻塞</b>赋值

    阻塞赋值与阻塞赋值

    ”=“阻塞赋值与”
    的头像 发表于 09-12 09:06 1045次阅读
    <b class='flag-5'>阻塞</b>赋值与<b class='flag-5'>非</b><b class='flag-5'>阻塞</b>赋值

    阻塞的的connect()函数如何编写

    阻塞的,直到三次握手建立之后,或者实在连不上超时返回,期间程序执行流一直阻塞在那里。那么如何利用connect()函数编写阻塞的连接代码呢? 无论在windows还是linux平台
    的头像 发表于 11-11 16:23 1567次阅读
    <b class='flag-5'>非</b><b class='flag-5'>阻塞</b>的的connect()函数如何编写

    verilog同步和异步的区别 verilog阻塞赋值和阻塞赋值的区别

    Verilog是一种硬件描述语言,用于设计和模拟数字电路。在Verilog中,同步和异步是用来描述数据传输和信号处理的两种不同方式,而阻塞赋值和阻塞赋值是两种不同的赋值方式。本文将详
    的头像 发表于 02-22 15:33 1739次阅读

    什么是阻塞阻塞

    什么是阻塞阻塞?我们就用管道的读写来举例子。
    的头像 发表于 03-25 10:04 505次阅读