0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

異步非阻塞框架是如何實(shí)現(xiàn)的

科技綠洲 ? 來(lái)源:Java技術(shù)指北 ? 作者:Java技術(shù)指北 ? 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):在對(duì)象之間定義一個(gè)一對(duì)多的依賴,當(dāng)一個(gè)對(duì)象狀態(tài)改變的時(shí)候,所有依賴的對(duì)象都會(huì)得到通知并自動(dòng)更新。

說(shuō)人話:也叫發(fā)布訂閱模式,能夠很好的解耦一個(gè)對(duì)象改變,自動(dòng)改變另一個(gè)對(duì)象這種情況。

2、觀察者模式定義

圖片

①、Subject 被觀察者

定義被觀察者必須實(shí)現(xiàn)的職責(zé), 它必須能夠動(dòng)態(tài)地增加、 取消觀察者。它一般是抽象類或者是實(shí)現(xiàn)類, 僅僅完成作為被觀察者必須實(shí)現(xiàn)的職責(zé):管理觀察者并通知觀察者。

②、Observer觀察者

觀察者接收到消息后, 即進(jìn)行update(更新方法) 操作, 對(duì)接收到的信息進(jìn)行處理。

③、ConcreteSubject具體的被觀察者

定義被觀察者自己的業(yè)務(wù)邏輯, 同時(shí)定義對(duì)哪些事件進(jìn)行通知。

④、ConcreteObserver具體的觀察者

每個(gè)觀察在接收到消息后的處理反應(yīng)是不同, 各個(gè)觀察者有自己的處理邏輯。

3、觀察者模式通用代碼

/**
 * 觀察者
 */
public interface Observer {
    // 更新方法
    void update();
}
/**
 * 具體觀察者
 */
public class ConcreteObserver implements Observer{
    @Override
    public void update() {
        System.out.println("接受到信息,并進(jìn)行處理");
    }
}
/**
 * 被觀察者
 */
public abstract class Subject {
    // 定義一個(gè)被觀察者數(shù)組
    private List< Observer > obsList = new ArrayList<  >();

    // 增加一個(gè)觀察者
    public void addObserver(Observer observer){
        obsList.add(observer);
    }

    // 刪除一個(gè)觀察者
    public void delObserver(Observer observer){
        obsList.remove(observer);
    }

    // 通知所有觀察者
    public void notifyObservers(){
        for (Observer observer : obsList){
            observer.update();
        }
    }
}
/**
 * 具體被觀察者
 */
public class ConcreteSubject extends Subject{
    // 具體的業(yè)務(wù)
    public void doSomething(){
        super.notifyObservers();
    }
}
public class ObserverClient {

    public static void main(String[] args) {
        // 創(chuàng)建一個(gè)被觀察者
        ConcreteSubject subject = new ConcreteSubject();
        // 定義一個(gè)觀察者
        Observer observer = new ConcreteObserver();
        // 觀察者觀察被觀察者
        subject.addObserver(observer);
        subject.doSomething();
    }
}

4、JDK 實(shí)現(xiàn)

在 JDK 的 java.util 包下,已經(jīng)為我們提供了觀察者模式的抽象實(shí)現(xiàn),感興趣的可以看看,內(nèi)部邏輯其實(shí)和我們上面介紹的差不多。

觀察者 java.util.Observer

圖片

被觀察者 java.util.Observable

圖片

圖片

5、實(shí)例

用戶進(jìn)行注冊(cè),注冊(cè)完成之后,會(huì)發(fā)一封歡迎郵件。

5.1 普通實(shí)現(xiàn)

圖片

public class UserController {

    public void register(String userName, String passWord){
        // 1、根據(jù)用戶名密碼保存在數(shù)據(jù)庫(kù)
        Long userId = saveUser(userName, passWord);
        // 2、如果上一步有結(jié)果則發(fā)送一封歡迎郵件
        if(userId != null){
            Mail.sendEmail(userId);
        }
    }


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

上面的注冊(cè)接口實(shí)現(xiàn)了兩件事,注冊(cè)和發(fā)送郵件,很明顯違反了單一職責(zé)原則,但假設(shè)這個(gè)注冊(cè)需求是不是經(jīng)常變動(dòng)的,這樣寫(xiě)也沒(méi)有什么問(wèn)題,但是假如需求變動(dòng),比如不僅要發(fā)送郵件,還得發(fā)送短信,那還這樣寫(xiě),那register接口會(huì)變得很復(fù)雜。

那應(yīng)該如何簡(jiǎn)化呢?沒(méi)錯(cuò),就是觀察者模式。

圖片

5.2 觀察者模式實(shí)現(xiàn)

我們直接套用 JDK 的實(shí)現(xiàn)。

import java.util.Observable;

/**
 * 用戶登錄——被觀察者
 */
public class UserControllerObservable extends Observable {

    public void register(String userName, String passWord){
        // 1、根據(jù)用戶名密碼保存在數(shù)據(jù)庫(kù)
        Long userId = saveUser(userName, passWord);
        // 2、如果上一步有結(jié)果則通知所有觀察者
        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;

/**
 * 發(fā)送郵件——觀察者
 */
public class MailObserver implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("發(fā)送郵件:" + arg + "歡迎你");
    }
}
/**
 * 發(fā)送手機(jī)短信——觀察者
 */
public class SMSObserver implements Observer {

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

測(cè)試:

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

通過(guò)觀察者模式改寫(xiě)后,后面用戶注冊(cè),就算在增加別的操作,我們也只需要增加一個(gè)觀察者即可,而注冊(cè)接口 register 不會(huì)有任何改動(dòng)。

5.3 異步模式優(yōu)化

在回到前面那張圖:

圖片

注冊(cè)之后進(jìn)行的兩步操作:發(fā)送郵件和發(fā)送短信,上面我們通過(guò)觀察者模式改寫(xiě)之后,雖然流程很清晰,但是我們發(fā)現(xiàn)是順序執(zhí)行的,但其實(shí)這兩步操作沒(méi)有先后順序,于是,我們可以改成異步模式,增加執(zhí)行效率。

/**
 * 發(fā)送郵件——觀察者
 */
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("發(fā)送郵件:" + arg + "歡迎你");
            }
        });

    }
}

5、EventBus

翻譯為“事件總線”,它提供了實(shí)現(xiàn)觀察者模式的骨架代碼。我們可以基于此框架,非常容易地在自己的業(yè)務(wù)場(chǎng)景中實(shí)現(xiàn)觀察者模式,不需要從零開(kāi)始開(kāi)發(fā)。其中,Google Guava EventBus 就是一個(gè)比較著名的 EventBus 框架,它不僅僅支持異步非阻塞模式,同時(shí)也支持同步阻塞模式。

PS:Google Guava 是一個(gè)特別好用的工具包,里面的代碼也都實(shí)現(xiàn)的比較優(yōu)雅,大家感興趣的可以研究研究源碼。

https://github.com/google/guava

下面我們以上面的例子來(lái)說(shuō)明如何使用 EventBus:

①、導(dǎo)如 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));
    }

    /**
     * 注意:泛型參數(shù)是 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、根據(jù)用戶名密碼保存在數(shù)據(jù)庫(kù)
        Long userId = saveUser(userName, passWord);
        // 2、如果上一步有結(jié)果則通知所有觀察者
        if(userId != null){
            eventBus.post(userName);
        }
    }


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

/**
 * 發(fā)送郵件——觀察者
 */
public class MailObserver{

    @Subscribe
    public void sendMail(String userName) {
        System.out.println("發(fā)送郵件:" + userName + "歡迎你");
    }
}
import com.google.common.eventbus.Subscribe;

/**
 * 發(fā)送手機(jī)短信——觀察者
 */
public class SMSObserver{

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

測(cè)試:

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 框架實(shí)現(xiàn)的觀察者模式,跟從零開(kāi)始編寫(xiě)的觀察者模式相比,從大的流程上來(lái)說(shuō),實(shí)現(xiàn)思路大致一樣,都需要定義 Observer,并且通過(guò) register() 函數(shù)注冊(cè) Observer,也都需要通過(guò)調(diào)用某個(gè)函數(shù)(比如,EventBus 中的 post() 函數(shù))來(lái)給 Observer 發(fā)送消息(在 EventBus 中消息被稱作事件 event)。但在實(shí)現(xiàn)細(xì)節(jié)方面,它們又有些區(qū)別。基于 EventBus,我們不需要定義 Observer 接口,任意類型的對(duì)象都可以注冊(cè)到 EventBus 中,通過(guò) @Subscribe 注解來(lái)標(biāo)明類中哪個(gè)函數(shù)可以接收被觀察者發(fā)送的消息。

6、觀察者模式優(yōu)點(diǎn)

①、觀察者和被觀察者之間是抽象耦合

不管是增加觀察者還是被觀察者都非常容易擴(kuò)展,在系統(tǒng)擴(kuò)展方面會(huì)得心應(yīng)手。

②、建立一套觸發(fā)機(jī)制

被觀察者變化引起觀察者自動(dòng)變化。但是需要注意的是,一個(gè)被觀察者,多個(gè)觀察者,Java的消息通知默認(rèn)是順序執(zhí)行的,如果一個(gè)觀察者卡住,會(huì)導(dǎo)致整個(gè)流程卡住,這就是同步阻塞。

所以實(shí)際開(kāi)發(fā)中沒(méi)有先后順序的考慮使用異步,異步非阻塞除了能夠?qū)崿F(xiàn)代碼解耦,還能充分利用硬件資源,提高代碼的執(zhí)行效率。

另外還有進(jìn)程間的觀察者模式,通?;谙㈥?duì)列來(lái)實(shí)現(xiàn),用于實(shí)現(xiàn)不同進(jìn)程間的觀察者和被觀察者之間的交互。

7、觀察者模式應(yīng)用場(chǎng)景

①、關(guān)聯(lián)行為場(chǎng)景。

②、事件多級(jí)觸發(fā)場(chǎng)景。

③、跨系統(tǒng)的消息交換場(chǎng)景, 如消息隊(duì)列的處理機(jī)制。

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 接口
    +關(guān)注

    關(guān)注

    33

    文章

    8633

    瀏覽量

    151367
  • 數(shù)據(jù)庫(kù)
    +關(guān)注

    關(guān)注

    7

    文章

    3821

    瀏覽量

    64506
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4798

    瀏覽量

    68725
  • JDK
    JDK
    +關(guān)注

    關(guān)注

    0

    文章

    81

    瀏覽量

    16602
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Verilog語(yǔ)言中阻塞阻塞賦值的不同

    來(lái)源:《Verilog數(shù)字系統(tǒng)設(shè)計(jì)(夏宇聞)》 阻塞阻塞賦值的語(yǔ)言結(jié)構(gòu)是Verilog 語(yǔ)言中最難理解概念之一。甚至有些很有經(jīng)驗(yàn)的Verilog 設(shè)計(jì)工程師也不能完全正確地理解:何時(shí)使用
    的頭像 發(fā)表于 08-17 16:18 ?6396次閱讀

    Verilog阻塞阻塞原理分析

    Verilog阻塞阻塞原理分析在Verilog語(yǔ)言最難弄明白的結(jié)構(gòu)中“阻塞賦值”要算一個(gè)。甚至是一些很有經(jīng)驗(yàn)的工程師也不完全明白“
    發(fā)表于 11-23 12:02

    同步與異步,阻塞阻塞的區(qū)別是什么

    同步與異步,阻塞阻塞的區(qū)別
    發(fā)表于 01-26 06:12

    怎么使用Select實(shí)現(xiàn)阻塞網(wǎng)絡(luò)編程?

    使用Select實(shí)現(xiàn)阻塞網(wǎng)絡(luò)編程
    發(fā)表于 03-30 07:34

    Java阻塞通信研究

    本文針對(duì)Java NIO 的特性做出分析與闡述,對(duì)網(wǎng)絡(luò)應(yīng)用中阻塞通信與阻塞通信、NIO的阻塞工作機(jī)制以及網(wǎng)絡(luò)通信中非
    發(fā)表于 08-10 10:15 ?18次下載

    verilog中阻塞賦值和阻塞賦值

    阻塞阻塞語(yǔ)句作為verilog HDL語(yǔ)言的最大難點(diǎn)之一,一直困擾著FPGA設(shè)計(jì)者,即使是一個(gè)頗富經(jīng)驗(yàn)的設(shè)計(jì)工程師,也很容易在這個(gè)點(diǎn)上犯下一些不必要的錯(cuò)誤。阻塞
    發(fā)表于 03-15 10:57 ?7004次閱讀

    深入理解阻塞阻塞賦值

    這是一個(gè)很好的學(xué)習(xí)阻塞阻塞的資料,對(duì)于FPGA的學(xué)習(xí)有很大幫助。
    發(fā)表于 04-22 11:00 ?11次下載

    簡(jiǎn)述阻塞賦值和阻塞賦值的可綜合性

    阻塞賦值和阻塞賦值的可綜合性 Blocking Assignment阻塞賦值和NonBlocking Assignment
    的頭像 發(fā)表于 05-12 09:45 ?2746次閱讀
    簡(jiǎn)述<b class='flag-5'>阻塞</b>賦值和<b class='flag-5'>非</b><b class='flag-5'>阻塞</b>賦值的可綜合性

    簡(jiǎn)述Verilog HDL中阻塞語(yǔ)句和阻塞語(yǔ)句的區(qū)別

    ? 在Verilog中有兩種類型的賦值語(yǔ)句:阻塞賦值語(yǔ)句(“=”)和阻塞賦值語(yǔ)句(“=”)。正確地使用這兩種賦值語(yǔ)句對(duì)于Verilog的設(shè)計(jì)和仿真非常重要。 Verilog語(yǔ)言中講的阻塞
    的頭像 發(fā)表于 12-02 18:24 ?6254次閱讀
    簡(jiǎn)述Verilog HDL中<b class='flag-5'>阻塞</b>語(yǔ)句和<b class='flag-5'>非</b><b class='flag-5'>阻塞</b>語(yǔ)句的區(qū)別

    時(shí)序邏輯中的阻塞阻塞

    Verilog HDL的賦值語(yǔ)句分為阻塞賦值和阻塞賦值兩種。阻塞賦值是指在當(dāng)前賦值完成前阻塞其他類型的賦值任務(wù),
    的頭像 發(fā)表于 03-15 13:53 ?3076次閱讀

    一文了解阻塞賦值與阻塞賦值

    今天給大家普及一下阻塞賦值和阻塞賦值的相關(guān)知識(shí)
    的頭像 發(fā)表于 07-07 14:15 ?2215次閱讀
    一文了解<b class='flag-5'>阻塞</b>賦值與<b class='flag-5'>非</b><b class='flag-5'>阻塞</b>賦值

    阻塞賦值與阻塞賦值

    ”=“阻塞賦值與”
    的頭像 發(fā)表于 09-12 09:06 ?1062次閱讀
    <b class='flag-5'>阻塞</b>賦值與<b class='flag-5'>非</b><b class='flag-5'>阻塞</b>賦值

    阻塞的的connect()函數(shù)如何編寫(xiě)

    阻塞的,直到三次握手建立之后,或者實(shí)在連不上超時(shí)返回,期間程序執(zhí)行流一直阻塞在那里。那么如何利用connect()函數(shù)編寫(xiě)阻塞的連接代碼呢? 無(wú)論在windows還是linux平臺(tái)
    的頭像 發(fā)表于 11-11 16:23 ?1593次閱讀
    <b class='flag-5'>非</b><b class='flag-5'>阻塞</b>的的connect()函數(shù)如何編寫(xiě)

    verilog同步和異步的區(qū)別 verilog阻塞賦值和阻塞賦值的區(qū)別

    Verilog是一種硬件描述語(yǔ)言,用于設(shè)計(jì)和模擬數(shù)字電路。在Verilog中,同步和異步是用來(lái)描述數(shù)據(jù)傳輸和信號(hào)處理的兩種不同方式,而阻塞賦值和阻塞賦值是兩種不同的賦值方式。本文將詳
    的頭像 發(fā)表于 02-22 15:33 ?1765次閱讀

    什么是阻塞阻塞?

    什么是阻塞阻塞?我們就用管道的讀寫(xiě)來(lái)舉例子。
    的頭像 發(fā)表于 03-25 10:04 ?515次閱讀