本文的主要內(nèi)容:
- 介紹中介者模式
- 數(shù)據(jù)同步示例
- 中介者模式總結(jié)
- 源碼分析中介者模式的典型應(yīng)用
- Java Timer 中的中介者模式
中介者模式
世界上存在著各種各樣的數(shù)據(jù)庫(kù)破讨,不同數(shù)據(jù)庫(kù)有各自的應(yīng)用場(chǎng)景,對(duì)于同一份數(shù)據(jù)烫沙,最開(kāi)始可能使用關(guān)系型數(shù)據(jù)庫(kù)(如MySQL)進(jìn)行存儲(chǔ)查詢,使用Redis作為緩存數(shù)據(jù)庫(kù)煤率,當(dāng)數(shù)據(jù)量較大時(shí)使用MySQL進(jìn)行查詢可能較慢,所以需要將數(shù)據(jù)同步到Elasticsearch或者列式數(shù)據(jù)庫(kù)如Hbase中進(jìn)行大數(shù)據(jù)查詢昼捍。
如何設(shè)計(jì)數(shù)據(jù)同步方案是一個(gè)重要的問(wèn)題妒茬。數(shù)據(jù)源眾多肛循,目標(biāo)端也眾多,設(shè)計(jì)得不好可能 "牽一發(fā)而動(dòng)全身"浩考。
如果我們這樣設(shè)計(jì):每個(gè)數(shù)據(jù)源直接同步數(shù)據(jù)到目標(biāo)端數(shù)據(jù)庫(kù)的搭伤,如果數(shù)據(jù)庫(kù)有 N 個(gè)闷畸,那么最多可能的同步作業(yè)將達(dá)到 N * N
個(gè),當(dāng)修改了其中一個(gè)數(shù)據(jù)庫(kù)的某些配置裁赠,可能需要修改另外的 N - 1
個(gè)數(shù)據(jù)庫(kù)的同步作業(yè)佩捞。
現(xiàn)在介紹另一種方案,DataX 是阿里巴巴集團(tuán)內(nèi)被廣泛使用的離線數(shù)據(jù)同步工具/平臺(tái)帘营,實(shí)現(xiàn)包括 MySQL芬迄、Oracle、SqlServer塞耕、Postgre荷科、HDFS、Hive、ADS蝎毡、HBase、TableStore(OTS)氧枣、MaxCompute(ODPS)沐兵、DRDS 等各種異構(gòu)數(shù)據(jù)源之間高效的數(shù)據(jù)同步功能。
DataX 其實(shí)相當(dāng)于一個(gè)中介便监,從數(shù)據(jù)源讀取數(shù)據(jù)扎谎,寫(xiě)入到目標(biāo)端,數(shù)據(jù)源不再需要維護(hù)到目標(biāo)端的同步作業(yè)烧董,只需要與 DataX 通信即可毁靶。DataX 體現(xiàn)了中介者模式的思想拐叉。
中介者模式(Mediator Pattern):用一個(gè)中介對(duì)象(中介者)來(lái)封裝一系列的對(duì)象交互,中介者使各對(duì)象不需要顯式地相互引用,從而使其耦合松散,而且可以獨(dú)立地改變它們之間的交互岳服。中介者模式又稱為調(diào)停者模式拖吼,它是一種對(duì)象行為型模式。
角色
Mediator(抽象中介者):它定義一個(gè)接口,該接口用于與各同事對(duì)象之間進(jìn)行通信。
ConcreteMediator(具體中介者):它是抽象中介者的子類,通過(guò)協(xié)調(diào)各個(gè)同事對(duì)象來(lái)實(shí)現(xiàn)協(xié)作行為,它維持了對(duì)各個(gè)同事對(duì)象的引用笆檀。
Colleague(抽象同事類):它定義各個(gè)同事類公有的方法樱衷,并聲明了一些抽象方法來(lái)供子類實(shí)現(xiàn),同時(shí)它維持了一個(gè)對(duì)抽象中介者類的引用涣达,其子類可以通過(guò)該引用來(lái)與中介者通信鸦概。
ConcreteColleague(具體同事類):它是抽象同事類的子類摄狱;每一個(gè)同事對(duì)象在需要和其他同事對(duì)象通信時(shí)踊跟,先與中介者通信,通過(guò)中介者來(lái)間接完成與其他同事類的通信沈矿;在具體同事類中實(shí)現(xiàn)了在抽象同事類中聲明的抽象方法醒颖。
中介者模式的核心在于中介者類的引入,在中介者模式中侵俗,中介者類承擔(dān)了兩方面的職責(zé):
- 中轉(zhuǎn)作用(結(jié)構(gòu)性):通過(guò)中介者提供的中轉(zhuǎn)作用澄耍,各個(gè)同事對(duì)象就不再需要顯式引用其他同事,當(dāng)需要和其他同事進(jìn)行通信時(shí),可通過(guò)中介者來(lái)實(shí)現(xiàn)間接調(diào)用。該中轉(zhuǎn)作用屬于中介者在結(jié)構(gòu)上的支持纽门。
- 協(xié)調(diào)作用(行為性):中介者可以更進(jìn)一步的對(duì)同事之間的關(guān)系進(jìn)行封裝败玉,同事可以一致的和中介者進(jìn)行交互沦补,而不需要指明中介者需要具體怎么做具壮,中介者根據(jù)封裝在自身內(nèi)部的協(xié)調(diào)邏輯,對(duì)同事的請(qǐng)求進(jìn)行進(jìn)一步處理,將同事成員之間的關(guān)系行為進(jìn)行分離和封裝溜腐。
示例
我們來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)化版的數(shù)據(jù)同步方案,有三種數(shù)據(jù)庫(kù) Mysql典蝌、Redis截驮、Elasticsearch,其中的 Mysql 作為主數(shù)據(jù)庫(kù),當(dāng)增加一條數(shù)據(jù)時(shí)需要同步到另外兩個(gè)數(shù)據(jù)庫(kù)中哲鸳;Redis 作為緩存數(shù)據(jù)庫(kù)逼龟,當(dāng)增加一條數(shù)據(jù)時(shí)不需要同步到另外另個(gè)數(shù)據(jù)庫(kù)评凝;而 Elasticsearch 作為大數(shù)據(jù)查詢數(shù)據(jù)庫(kù),有一個(gè)統(tǒng)計(jì)功能审轮,當(dāng)增加一條數(shù)據(jù)時(shí)只需要同步到 Mysql肥哎,所以它們之間的關(guān)系圖如下所示。
首先我們來(lái)實(shí)現(xiàn)第一種不使用中介者模式的數(shù)據(jù)同步方案疾渣,各數(shù)據(jù)源維護(hù)各自的同步作業(yè)。
抽象數(shù)據(jù)庫(kù)
public abstract class AbstractDatabase {
public abstract void add(String data);
public abstract void addData(String data);
}
具體數(shù)據(jù)庫(kù) Mysql崖飘,維護(hù)同步到 Redis和Elasticsearch 的同步作業(yè)
public class MysqlDatabase extends AbstractDatabase {
private List<String> dataset = new ArrayList<String>();
@Setter
private RedisDatabase redisDatabase;
@Setter
private EsDatabase esDatabase;
@Override
public void addData(String data) {
System.out.println("Mysql 添加數(shù)據(jù):" + data);
this.dataset.add(data);
}
@Override
public void add(String data) {
addData(data);
this.redisDatabase.addData(data); // 維護(hù)同步到Redis的同步作業(yè)
this.esDatabase.addData(data); // 維護(hù)同步到Elasticsearch的同步作業(yè)
}
public void select() {
System.out.println("- Mysql 查詢榴捡,數(shù)據(jù):" + this.dataset.toString());
}
}
具體數(shù)據(jù)庫(kù) Redis,不需要同步到其它數(shù)據(jù)庫(kù)
public class RedisDatabase extends AbstractDatabase {
private List<String> dataset = new LinkedList<String>();
@Override
public void addData(String data) {
System.out.println("Redis 添加數(shù)據(jù):" + data);
this.dataset.add(data);
}
@Override
public void add(String data) {
addData(data); // 不同步到其它數(shù)據(jù)庫(kù)
}
public void cache() {
System.out.println("- Redis 緩存的數(shù)據(jù):" + this.dataset.toString());
}
}
Elasticsearch 朱浴,只需要同步到Mysql
public class EsDatabase extends AbstractDatabase {
private List<String> dataset = new CopyOnWriteArrayList<String>();
@Setter
private MysqlDatabase mysqlDatabase;
@Override
public void addData(String data) {
System.out.println("ES 添加數(shù)據(jù):" + data);
this.dataset.add(data);
}
@Override
public void add(String data) {
addData(data);
this.mysqlDatabase.addData(data); // 維護(hù)同步到MySQL的同步作業(yè)
}
public void count() {
int count = this.dataset.size();
System.out.println("- Elasticsearch 統(tǒng)計(jì)吊圾,目前有 " + count + " 條數(shù)據(jù),數(shù)據(jù):" + this.dataset.toString());
}
}
測(cè)試客戶端翰蠢,分別往三個(gè)數(shù)據(jù)庫(kù)中加入一些數(shù)據(jù)查看同步效果
public class Client {
public static void main(String[] args) {
MysqlDatabase mysqlDatabase = new MysqlDatabase();
RedisDatabase redisDatabase = new RedisDatabase();
EsDatabase esDatabase = new EsDatabase();
mysqlDatabase.setRedisDatabase(redisDatabase);
mysqlDatabase.setEsDatabase(esDatabase);
esDatabase.setMysqlDatabase(mysqlDatabase);
System.out.println("\n---------mysql 添加數(shù)據(jù) 1项乒,將同步到Redis和ES中-----------");
mysqlDatabase.add("1");
mysqlDatabase.select();
redisDatabase.cache();
esDatabase.count();
System.out.println("\n---------Redis添加數(shù)據(jù) 2,將不同步到其它數(shù)據(jù)庫(kù)-----------");
redisDatabase.add("2");
mysqlDatabase.select();
redisDatabase.cache();
esDatabase.count();
System.out.println("\n---------ES 添加數(shù)據(jù) 3梁沧,只同步到 Mysql-----------");
esDatabase.add("3");
mysqlDatabase.select();
redisDatabase.cache();
esDatabase.count();
}
}
輸出結(jié)果
---------mysql 添加數(shù)據(jù) 1檀何,將同步到Redis和ES中-----------
Mysql 添加數(shù)據(jù):1
Redis 添加數(shù)據(jù):1
ES 添加數(shù)據(jù):1
- Mysql 查詢,數(shù)據(jù):[1]
- Redis 緩存的數(shù)據(jù):[1]
- Elasticsearch 統(tǒng)計(jì)廷支,目前有 1 條數(shù)據(jù)频鉴,數(shù)據(jù):[1]
---------Redis添加數(shù)據(jù) 2,將不同步到其它數(shù)據(jù)庫(kù)-----------
Redis 添加數(shù)據(jù):2
- Mysql 查詢恋拍,數(shù)據(jù):[1]
- Redis 緩存的數(shù)據(jù):[1, 2]
- Elasticsearch 統(tǒng)計(jì)垛孔,目前有 1 條數(shù)據(jù),數(shù)據(jù):[1]
---------ES 添加數(shù)據(jù) 3施敢,只同步到 Mysql-----------
ES 添加數(shù)據(jù):3
Mysql 添加數(shù)據(jù):3
- Mysql 查詢周荐,數(shù)據(jù):[1, 3]
- Redis 緩存的數(shù)據(jù):[1, 2]
- Elasticsearch 統(tǒng)計(jì),目前有 2 條數(shù)據(jù)僵娃,數(shù)據(jù):[1, 3]
其實(shí)這樣已經(jīng)實(shí)現(xiàn)了我們的需求概作,但是存在一些問(wèn)題:
- 系統(tǒng)結(jié)構(gòu)復(fù)雜且耦合度高。數(shù)據(jù)源需要維護(hù)目標(biāo)端數(shù)據(jù)庫(kù)的引用悯许,以便完成數(shù)據(jù)同步
- 組件的可重用性差仆嗦。由于每一個(gè)數(shù)據(jù)源和目標(biāo)端之間具有很強(qiáng)的關(guān)聯(lián),若沒(méi)有目標(biāo)端的支持先壕,這個(gè)組件很難被另一個(gè)系統(tǒng)或模塊重用
- 系統(tǒng)的可擴(kuò)展性差:如果需要增加瘩扼、修改或刪除其中一個(gè)數(shù)據(jù)庫(kù)谆甜、將導(dǎo)致多個(gè)類的源代碼需要修改,這違反了 "開(kāi)閉原則"集绰,可擴(kuò)展性和靈活性欠佳规辱。
我們使用中介者模式來(lái)重構(gòu),將數(shù)據(jù)同步的功能遷移到中介者中栽燕,由中介者來(lái)管理數(shù)據(jù)同步作業(yè)
首先還是抽象數(shù)據(jù)庫(kù)類(抽象同事類)罕袋,維護(hù)了一個(gè)中介者
public abstract class AbstractDatabase {
public static final String MYSQL = "mysql";
public static final String REDIS = "redis";
public static final String ELASTICSEARCH = "elasticsearch";
protected AbstractMediator mediator; // 中介者
public AbstractDatabase(AbstractMediator mediator) {
this.mediator = mediator;
}
public abstract void addData(String data);
public abstract void add(String data);
}
Mysql 數(shù)據(jù)庫(kù)(具體同事類)
public class MysqlDatabase extends AbstractDatabase {
private List<String> dataset = new ArrayList<String>();
public MysqlDatabase(AbstractMediator mediator) {
super(mediator);
}
@Override
public void addData(String data) {
System.out.println("Mysql 添加數(shù)據(jù):" + data);
this.dataset.add(data);
}
@Override
public void add(String data) {
addData(data);
this.mediator.sync(AbstractDatabase.MYSQL, data); // 數(shù)據(jù)同步作業(yè)交給中介者管理
}
public void select() {
System.out.println("Mysql 查詢,數(shù)據(jù):" + this.dataset.toString());
}
}
Redis 數(shù)據(jù)庫(kù)(具體同事類)
public class RedisDatabase extends AbstractDatabase {
private List<String> dataset = new LinkedList<String>();
public RedisDatabase(AbstractMediator mediator) {
super(mediator);
}
@Override
public void addData(String data) {
System.out.println("Redis 添加數(shù)據(jù):" + data);
this.dataset.add(data);
}
@Override
public void add(String data) {
addData(data);
this.mediator.sync(AbstractDatabase.REDIS, data); // 數(shù)據(jù)同步作業(yè)交給中介者管理
}
public void cache() {
System.out.println("Redis 緩存的數(shù)據(jù):" + this.dataset.toString());
}
}
Elasticsearch(具體同事類)
public class EsDatabase extends AbstractDatabase {
private List<String> dataset = new CopyOnWriteArrayList<String>();
public EsDatabase(AbstractMediator mediator) {
super(mediator);
}
@Override
public void addData(String data) {
System.out.println("ES 添加數(shù)據(jù):" + data);
this.dataset.add(data);
}
@Override
public void add(String data) {
addData(data);
this.mediator.sync(AbstractDatabase.ELASTICSEARCH, data); // 數(shù)據(jù)同步作業(yè)交給中介者管理
}
public void count() {
int count = this.dataset.size();
System.out.println("Elasticsearch 統(tǒng)計(jì)碍岔,目前有 " + count + " 條數(shù)據(jù)浴讯,數(shù)據(jù):" + this.dataset.toString());
}
}
抽象中介者
@Data
public abstract class AbstractMediator {
protected MysqlDatabase mysqlDatabase;
protected RedisDatabase redisDatabase;
protected EsDatabase esDatabase;
public abstract void sync(String databaseName, String data);
}
具體中介者
public class SyncMediator extends AbstractMediator {
@Override
public void sync(String databaseName, String data) {
if (AbstractDatabase.MYSQL.equals(databaseName)) {
// mysql 同步到 redis 和 Elasticsearch
this.redisDatabase.addData(data);
this.esDatabase.addData(data);
} else if (AbstractDatabase.REDIS.equals(databaseName)) {
// redis 緩存同步,不需要同步到其他數(shù)據(jù)庫(kù)
} else if (AbstractDatabase.ELASTICSEARCH.equals(databaseName)) {
// Elasticsearch 同步到 Mysql
this.mysqlDatabase.addData(data);
}
}
}
測(cè)試客戶端
public class Client {
public static void main(String[] args) {
AbstractMediator syncMediator = new SyncMediator();
MysqlDatabase mysqlDatabase = new MysqlDatabase(syncMediator);
RedisDatabase redisDatabase = new RedisDatabase(syncMediator);
EsDatabase esDatabase = new EsDatabase(syncMediator);
syncMediator.setMysqlDatabase(mysqlDatabase);
syncMediator.setRedisDatabase(redisDatabase);
syncMediator.setEsDatabase(esDatabase);
System.out.println("\n---------mysql 添加數(shù)據(jù) 1蔼啦,將同步到Redis和ES中-----------");
mysqlDatabase.add("1");
mysqlDatabase.select();
redisDatabase.cache();
esDatabase.count();
System.out.println("\n---------Redis添加數(shù)據(jù) 2榆纽,將不同步到其它數(shù)據(jù)庫(kù)-----------");
redisDatabase.add("2");
mysqlDatabase.select();
redisDatabase.cache();
esDatabase.count();
System.out.println("\n---------ES 添加數(shù)據(jù) 3,只同步到 Mysql-----------");
esDatabase.add("3");
mysqlDatabase.select();
redisDatabase.cache();
esDatabase.count();
}
}
輸出結(jié)果捏肢,與預(yù)期一致
---------mysql 添加數(shù)據(jù) 1奈籽,將同步到Redis和ES中-----------
Mysql 添加數(shù)據(jù):1
Redis 添加數(shù)據(jù):1
ES 添加數(shù)據(jù):1
- Mysql 查詢,數(shù)據(jù):[1]
- Redis 緩存的數(shù)據(jù):[1]
- Elasticsearch 統(tǒng)計(jì)鸵赫,目前有 1 條數(shù)據(jù)衣屏,數(shù)據(jù):[1]
---------Redis添加數(shù)據(jù) 2,將不同步到其它數(shù)據(jù)庫(kù)-----------
Redis 添加數(shù)據(jù):2
- Mysql 查詢辩棒,數(shù)據(jù):[1]
- Redis 緩存的數(shù)據(jù):[1, 2]
- Elasticsearch 統(tǒng)計(jì)狼忱,目前有 1 條數(shù)據(jù),數(shù)據(jù):[1]
---------ES 添加數(shù)據(jù) 3盗温,只同步到 Mysql-----------
ES 添加數(shù)據(jù):3
Mysql 添加數(shù)據(jù):3
- Mysql 查詢藕赞,數(shù)據(jù):[1, 3]
- Redis 緩存的數(shù)據(jù):[1, 2]
- Elasticsearch 統(tǒng)計(jì),目前有 2 條數(shù)據(jù)卖局,數(shù)據(jù):[1, 3]
畫(huà)出類圖如下
中介者模式總結(jié)
中介者模式的主要優(yōu)點(diǎn)
中介者模式簡(jiǎn)化了對(duì)象之間的交互斧蜕,它用中介者和同事的一對(duì)多交互代替了原來(lái)同事之間的多對(duì)多交互,一對(duì)多關(guān)系更容易理解砚偶、維護(hù)和擴(kuò)展批销,將原本難以理解的網(wǎng)狀結(jié)構(gòu)轉(zhuǎn)換成相對(duì)簡(jiǎn)單的星型結(jié)構(gòu)。
中介者模式可將各同事對(duì)象解耦染坯。中介者有利于各同事之間的松耦合均芽,我們可以獨(dú)立的改變和復(fù)用每一個(gè)同事和中介者,增加新的中介者和新的同事類都比較方便单鹿,更好地符合 "開(kāi)閉原則"掀宋。
可以減少子類生成,中介者將原本分布于多個(gè)對(duì)象間的行為集中在一起,改變這些行為只需生成新的中介者子類即可劲妙,這使各個(gè)同事類可被重用湃鹊,無(wú)須對(duì)同事類進(jìn)行擴(kuò)展。
中介者模式的主要缺點(diǎn)
- 在具體中介者類中包含了大量同事之間的交互細(xì)節(jié)镣奋,可能會(huì)導(dǎo)致具體中介者類非常復(fù)雜币呵,使得系統(tǒng)難以維護(hù)。(也就是把具體同事類之間的交互復(fù)雜性集中到了中介者類中侨颈,結(jié)果中介者成了最復(fù)雜的類)
適用場(chǎng)景
系統(tǒng)中對(duì)象之間存在復(fù)雜的引用關(guān)系余赢,系統(tǒng)結(jié)構(gòu)混亂且難以理解。
一個(gè)對(duì)象由于引用了其他很多對(duì)象并且直接和這些對(duì)象通信哈垢,導(dǎo)致難以復(fù)用該對(duì)象妻柒。
想通過(guò)一個(gè)中間類來(lái)封裝多個(gè)類中的行為,而又不想生成太多的子類温赔「蛏荩可以通過(guò)引入中介者類來(lái)實(shí)現(xiàn),在中介者中定義對(duì)象交互的公共行為陶贼,如果需要改變行為則可以增加新的具體中介者類。
中介者模式的典型應(yīng)用
Java Timer 中的中介者模式
敲一個(gè) java.util.Timer
的Demo
兩個(gè)任務(wù)類
public class MyOneTask extends TimerTask {
private static int num = 0;
@Override
public void run() {
System.out.println("I'm MyOneTask " + ++num);
}
}
public class MyTwoTask extends TimerTask {
private static int num = 1000;
@Override
public void run() {
System.out.println("I'm MyTwoTask " + num--);
}
}
客戶端測(cè)試待秃,3秒后開(kāi)始執(zhí)行拜秧,循環(huán)周期為 1秒
public class TimerTest {
public static void main(String[] args) {
// 注意:多線程并行處理定時(shí)任務(wù)時(shí),Timer運(yùn)行多個(gè)TimeTask時(shí)章郁,只要其中之一沒(méi)有捕獲拋出的異常枉氮,
// 其它任務(wù)便會(huì)自動(dòng)終止運(yùn)行,使用ScheduledExecutorService則沒(méi)有這個(gè)問(wèn)題
Timer timer = new Timer();
timer.schedule(new MyOneTask(), 3000, 1000); // 3秒后開(kāi)始運(yùn)行暖庄,循環(huán)周期為 1秒
timer.schedule(new MyTwoTask(), 3000, 1000);
}
}
輸出
I'm MyOneTask 1
I'm MyTwoTask 1000
I'm MyTwoTask 999
I'm MyOneTask 2
I'm MyOneTask 3
I'm MyTwoTask 998
I'm MyTwoTask 997
I'm MyOneTask 4
I'm MyOneTask 5
I'm MyTwoTask 996
I'm MyTwoTask 995
I'm MyOneTask 6
...
Timer
的部分關(guān)鍵源碼如下
public class Timer {
private final TaskQueue queue = new TaskQueue();
private final TimerThread thread = new TimerThread(queue);
public void schedule(TimerTask task, long delay) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
sched(task, System.currentTimeMillis()+delay, 0);
}
public void schedule(TimerTask task, Date time) {
sched(task, time.getTime(), 0);
}
private void sched(TimerTask task, long time, long period) {
if (time < 0)
throw new IllegalArgumentException("Illegal execution time.");
if (Math.abs(period) > (Long.MAX_VALUE >> 1))
period >>= 1;
// 獲取任務(wù)隊(duì)列的鎖(同一個(gè)線程多次獲取這個(gè)鎖并不會(huì)被阻塞,不同線程獲取時(shí)才可能被阻塞)
synchronized(queue) {
// 如果定時(shí)調(diào)度線程已經(jīng)終止了,則拋出異常結(jié)束
if (!thread.newTasksMayBeScheduled)
throw new IllegalStateException("Timer already cancelled.");
// 再獲取定時(shí)任務(wù)對(duì)象的鎖(為什么還要再加這個(gè)鎖呢?想不清)
synchronized(task.lock) {
// 判斷線程的狀態(tài),防止多線程同時(shí)調(diào)度到一個(gè)任務(wù)時(shí)多次被加入任務(wù)隊(duì)列
if (task.state != TimerTask.VIRGIN)
throw new IllegalStateException(
"Task already scheduled or cancelled");
// 初始化定時(shí)任務(wù)的下次執(zhí)行時(shí)間
task.nextExecutionTime = time;
// 重復(fù)執(zhí)行的間隔時(shí)間
task.period = period;
// 將定時(shí)任務(wù)的狀態(tài)由TimerTask.VIRGIN(一個(gè)定時(shí)任務(wù)的初始化狀態(tài))設(shè)置為T(mén)imerTask.SCHEDULED
task.state = TimerTask.SCHEDULED;
}
// 將任務(wù)加入任務(wù)隊(duì)列
queue.add(task);
// 如果當(dāng)前加入的任務(wù)是需要第一個(gè)被執(zhí)行的(也就是他的下一次執(zhí)行時(shí)間離現(xiàn)在最近)
// 則喚醒等待queue的線程(對(duì)應(yīng)到上面提到的queue.wait())
if (queue.getMin() == task)
queue.notify();
}
}
// cancel會(huì)等到所有定時(shí)任務(wù)執(zhí)行完后立刻終止定時(shí)線程
public void cancel() {
synchronized(queue) {
thread.newTasksMayBeScheduled = false;
queue.clear();
queue.notify(); // In case queue was already empty.
}
}
// ...
}
Timer
中在 schedulexxx
方法中通過(guò) TaskQueue
協(xié)調(diào)各種 TimerTask
定時(shí)任務(wù)聊替,Timer
是中介者,TimerTask
是抽象同事類培廓,而我們自己寫(xiě)的任務(wù)則是具體同事類
TimerThread
是 Timer
中定時(shí)調(diào)度線程類的定義惹悄,這個(gè)類會(huì)做為一個(gè)線程一直運(yùn)行來(lái)執(zhí)行 Timer
中任務(wù)隊(duì)列中的任務(wù)。
Timer
這個(gè)中介者的功能就是定時(shí)調(diào)度我們寫(xiě)的各種任務(wù)肩钠,將任務(wù)添加到 TaskQueue
任務(wù)隊(duì)列中泣港,給 TimerThread
執(zhí)行,讓任務(wù)與執(zhí)行線程解耦
其他的中介者模式應(yīng)用
java.util.concurrent.Executor#execute
和java.util.concurrent.ExecutorService#submit
與Timer#schedule
類似MVC模式中价匠,Controller 是中介者当纱,根據(jù) View 層的請(qǐng)求來(lái)操作 Model 層
參考:
劉偉:設(shè)計(jì)模式Java版
慕課網(wǎng)java設(shè)計(jì)模式精講 Debug 方式+內(nèi)存分析
java.util系列源碼解讀之Timer定時(shí)器
后記
歡迎評(píng)論、轉(zhuǎn)發(fā)踩窖、分享坡氯,您的支持是我最大的動(dòng)力
更多內(nèi)容可訪問(wèn)我的個(gè)人博客:http://laijianfeng.org
關(guān)注【小旋鋒】微信公眾號(hào),及時(shí)接收博文推送
推薦閱讀
設(shè)計(jì)模式 | 享元模式及典型應(yīng)用
設(shè)計(jì)模式 | 組合模式及典型應(yīng)用
設(shè)計(jì)模式 | 模板方法模式及典型應(yīng)用
設(shè)計(jì)模式 | 迭代器模式及典型應(yīng)用
設(shè)計(jì)模式 | 策略模式及典型應(yīng)用
設(shè)計(jì)模式 | 觀察者模式及典型應(yīng)用
設(shè)計(jì)模式 | 備忘錄模式及典型應(yīng)用