設計理念——為何而存在
先來一發源碼。
public interface Executor {
void execute(Runnable command);
}
就是這么短。那么這么短的一個接口存在的意義是什么呢?
下面就來分析一下。
當我們想創建并執行一個線程的時候,通常是這樣的。
new Thread(new(RunnableTask())).start()
是不是短!平!快!。這樣不是很好嗎?但是使用這種方式,我們無法控制程序中線程的數量,創建過多的線程會帶來性能問題。我們需要一種方式控制線程的數量,有時我們還希望控制各個任務之間調度關系。Executor接口正式為解決這個問題而來。
Executor接口官方介紹:An object that executes submitted Runnable tasks. This interface provides a way of decoupling task submission from the mechanics of how each task will be run, including details of thread use, scheduling, etc. An Executor is normally used instead of explicitly creating threads.
意思:Executor是用來提交Runnable的。它可以將Runnable的遞交、使用的細節、調度與執行解耦。用來代替直接創建Thread的方式。
不容易理解?莫關系!下面通過一個實際應用來解釋這段話。
比如,你想保證任務的順序執行(每個時刻只有一個Runnable在執行,只有上一個Runnable執行完畢之后才會執行下一個Runnable)并且為每一個Runnable創建一個新的Thread。我們可以這樣做:
//SerialExecutor提供調度功能,將用戶提交的Runnable保存到一個隊列里面,然后順序執行
class SerialExecutor implements Executor {
//隊列,用來存放提交進來的Runnable
final Queue<Runnable> tasks = new ArrayDeque<Runnable>();
//提交進來的Runnable實際執行的位置。
final Executor executor;
//當前正在執行的Runnable
Runnable active;
SerialExecutor(Executor executor) {
this.executor = executor;
}
public synchronized void execute(final Runnable r) {
//將Runnable包裝,并添加到隊里中。
tasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
//執行完畢后,會自動調用隊里中的下一個Runnable
scheduleNext();
}
}
});
if (active == null) {
//首次調度
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((active = tasks.poll()) != null) {
executor.execute(active);
}
}
}}
//ThreadPerTaskExecutor是Runnable的實際執行的地方。為每一個Runnable創建一個Thread。
class ThreadPerTaskExecutor implements Executor {
public void execute(Runnable r) {
new Thread(r).start();
}
}}
//執行:只有任務1執行完畢后才會執行任務2
Executor executor = new SerialExecutor(new ThreadPerTaskExecutor());
executor.execute(new Runnable(){
public void run() {
//任務1...
}
});
executor.execute(new Runnable(){
public void run() {
//任務2...
}
});
在這個例子中,SerialExecutor用于Runnable的調度,它將Runnable進行簡單的包裝,并保存到隊里中。經過包裝后,不僅會執行原始提交Runnable的代碼,并且會執行結束后調用隊列中的下一個Runnable。就這樣,SerialExecutor實現了Runnable的順序執行。而ThreadPerTaskExecutor類則是用來控制Runnable執行細節的地方,在這里,我們簡單的為每個Runnable創建了新的Thread,在實際應用中,你可以使用線程池來復用Thread。
總結
Executor被創造的原因:將任務的執行、任務的調度、任務的執行細節進行解耦。
- 任務是如何調度的,你是想一個個的順序執行,還是想多個任務一起并發并能自由的控制并發數量上線,都可以通過自己實現Executor接口進行定制。你只需要將任務通過Executor#execute(Runnable)方法提交進來就可以了,具體的調度方案,Runnable不需要關注。這樣就實現了任務執行和調度的解耦。
- 對于任務的執行細節,你是想為每個任務都創建新的線程,還是想復用已有的線程,也可以通過實現Executor接口進行定制。同樣,你只需要將任務通過Executor#execute(Runnable)方法提交進來就可以了,具體的執行細節,Runnable不需要關注。這樣就實現了任務執行和執行細節的解耦。
設計模式的使用:中介者模式
在這里,使用了設計模式中的中介者模式,下面是中介者模式的定義和UML類圖。
中介者模式(Mediator Pattern):定義一個中介對象來封裝系列對象之間的交互。中介者使各個對象不需要顯示地相互引用,從而使其耦合性松散,而且可以獨立地改變他們之間的交互。
- 抽象中介者(Mediator)角色:抽象中介者角色定義統一的接口用于各同事角色之間的通信。
- 具體中介者(Concrete Mediator)角色:具體中介者角色通過協調各同事角色實現協作行為。為此它要知道并引用各個同事角色。
- 同事(Colleague)角色:每一個同事角色都知道對應的具體中介者角色,而且與其他的同事角色通信的時候,一定要通過中介者角色協作。——《設計模式》
UML類圖:
其中Executor就是一個抽象中介者角色,Executor的具體實現就是一個具體中介者角色,每個Runnable就相當于同事角色,Executor的execute(Runnable...)方法即是各個Runnable之間通信的接口,每個Runnable都被提交到Executor的具體實現類中,由Executor的具體實現類來協調各Runnable之間的實現協作行為。