作者:牛牛碼特 鏈接:https://juejin.cn/post/6844903929281511438 背景 緩存是軟體開發中一個非常有用的概念,資料庫緩存更是在項目中必然會遇到的場景。而緩存一致性的保證,更是在面試中被反覆問到,這裡進行一下總結,針對不同的要求,選擇恰到好處的一致性方案。 緩存 ...
Google Guava 工具類(一)—— EventBus(觀察者模式的實現)
❗❗❗ 未解決的問題:
AsyncEventBus
的併發執行
EventBus 是設計模式中的觀察者模式(生產者/消費者編程模型)的實現。
在學習 EventBus 之前,先瞭解一下其涉及到的相關術語
EvenBus 中的相關術語
EventBus 術語 | 解釋 | 備註 |
---|---|---|
事件(消息) | 可以向事件匯流排(EventBus)發佈的對象 | 通常是一個類,不同的消息事件用不同的類來代替,消息內容就是類裡面的屬性 |
訂閱 | 向事件匯流排註冊監聽者,以接受事件的行為 | EventBus.register(Object),參數就是監聽者 |
監聽者 | 提供一個處理方法,希望接受和處理事件的對象 | 通常也是一個類,裡面有消息的處理方法 |
處理方法 | 監聽者提供的公共方法,事件匯流排使用該方法向監聽者發送事件;該方法應使用 Subscribe 註解 | 監聽者裡面添加一個 Subscribe 註解的方法,就可以認為是消息的處理方法 |
發佈消息 | 通過事件匯流排向所有匹配的監聽者提供事件 | EventBus.post(Object) |
EvenBus 的簡單使用
添加依賴
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
EventBus 的使用很簡單,籠統來說可分為以下幾個步驟。
- 創建 EventBus 對象。通常全局或模塊內通過單例模式只用一個 EventBus 對象
- 創建消息類
- 創建監聽者類
- 註冊監聽者類。如果有多個 EventBus 對象,監聽者類註冊在哪個 EventBus 對象下,消息就需要發佈到對應的 EventBus 中
- 發佈消息
EventBusDemo.java
package cn.jkingtools.demo.guava.eventbus;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
public class EventBusDemo {
// 1. 創建消息類
private static class Message {
private String message;
public Message(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
// 2. 創建監聽者類,即事件處理函數,需要使用 @Subscribe 進行註解
private static class EventListener {
@Subscribe
public void dealWithEvent(Message msg) {
System.out.println("接收消息" + msg.getMessage());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("處理消息" + msg.getMessage());
}
}
public static void main(String[] args) {
// 3. 創建 EventBus 對象
EventBus eventBus = new EventBus("Test");
// 4. 註冊監聽者類
eventBus.register(new EventListener());
// 5. 發佈消息
for (int i=0; i<5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("---------");
eventBus.post(new Message("tttt" + i));
}
}
}
執行輸出信息:
---------
接收消息tttt0
處理消息tttt0
---------
接收消息tttt1
處理消息tttt1
---------
接收消息tttt2
處理消息tttt2
---------
接收消息tttt3
處理消息tttt3
---------
接收消息tttt4
處理消息tttt4
在上面的代碼中,添加了幾個 sleep,在實際運行的時候註意輸出,所有的事件消息都是順序輸出的。這是因為事件的發送方和事件消費方都在一個線程中,事件發送方只有在發送的事件處理完畢後才會繼續執行自己後面的代碼。這裡可使用 AsyncEventBus
類實現事件的非同步處理,也就是將事件處理放到一個線程池裡面去執行。
AsyncEventBus(EventBus 的非同步實現)
com.google.common.eventbus.AsyncEventBus
是 EventBus 的非同步實現,即將事件放到一個單獨的線程池中去執行,只需要將實例化的 EventBus
對象換成 AsyncEventBus
即可,並傳入一個線程池對象。
AsyncEventBus asyncEventBus = new AsyncEventBus("", Executors.newCachedThreadPool());
❗❗❗ 我的測試源碼如下,但未實現事件的並行執行。
package cn.jkingtools.demo.guava.eventbus; import com.google.common.eventbus.AsyncEventBus; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import java.util.concurrent.Executors; public class AsyncEventBusDemo { // 1. 創建消息類 private static class Message { private String message; public Message(String message) { this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } // 2. 創建監聽者類,即事件處理函數,需要使用 @Subscribe 進行註解 private static class EventListener { @Subscribe public void dealWithEvent(Message msg) { System.out.println("接收消息" + msg.getMessage()); try { Thread.sleep(10000); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("處理消息" + msg.getMessage()); } } public static void main(String[] args) { // 3. 創建 EventBus 對象 AsyncEventBus eventBus = new AsyncEventBus("Test", Executors.newCachedThreadPool()); // 4. 註冊監聽者類 eventBus.register(new EventListener()); // 5. 發佈消息 for (int i=0; i<10; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("---------"); eventBus.post(new Message("tttt" + i)); } } }
輸出信息如下:
--------- 接收消息tttt0 --------- --------- --------- --------- 處理消息tttt0 接收消息tttt4 處理消息tttt4 接收消息tttt3 處理消息tttt3 接收消息tttt2 處理消息tttt2 接收消息tttt1 處理消息tttt1
通過觀察輸出,AsyncEventBus 雖然並未在主線程阻塞,但事件卻是被順序執行的,並未實現併發。