Android concurrent(一)—— Executor接口

設計理念——為何而存在

先來一發源碼。

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被創造的原因:將任務的執行、任務的調度、任務的執行細節進行解耦。

  1. 任務是如何調度的,你是想一個個的順序執行,還是想多個任務一起并發并能自由的控制并發數量上線,都可以通過自己實現Executor接口進行定制。你只需要將任務通過Executor#execute(Runnable)方法提交進來就可以了,具體的調度方案,Runnable不需要關注。這樣就實現了任務執行和調度的解耦。
  2. 對于任務的執行細節,你是想為每個任務都創建新的線程,還是想復用已有的線程,也可以通過實現Executor接口進行定制。同樣,你只需要將任務通過Executor#execute(Runnable)方法提交進來就可以了,具體的執行細節,Runnable不需要關注。這樣就實現了任務執行和執行細節的解耦。

設計模式的使用:中介者模式

在這里,使用了設計模式中的中介者模式,下面是中介者模式的定義和UML類圖。

中介者模式(Mediator Pattern):定義一個中介對象來封裝系列對象之間的交互。中介者使各個對象不需要顯示地相互引用,從而使其耦合性松散,而且可以獨立地改變他們之間的交互。

  1. 抽象中介者(Mediator)角色:抽象中介者角色定義統一的接口用于各同事角色之間的通信。
  2. 具體中介者(Concrete Mediator)角色:具體中介者角色通過協調各同事角色實現協作行為。為此它要知道并引用各個同事角色。
  3. 同事(Colleague)角色:每一個同事角色都知道對應的具體中介者角色,而且與其他的同事角色通信的時候,一定要通過中介者角色協作。——《設計模式》

UML類圖:

Mediator_Pattern.jpg

其中Executor就是一個抽象中介者角色,Executor的具體實現就是一個具體中介者角色,每個Runnable就相當于同事角色,Executor的execute(Runnable...)方法即是各個Runnable之間通信的接口,每個Runnable都被提交到Executor的具體實現類中,由Executor的具體實現類來協調各Runnable之間的實現協作行為。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 一.線程與進程相關 1.進程 ??定義:進程是具有獨立功能的程序關于某個數據集合上的一次運行活動,進程是操作系統分...
    Geeks_Liu閱讀 1,748評論 2 4
  • 下面是我自己收集整理的Java線程相關的面試題,可以用它來好好準備面試。 參考文檔:-《Java核心技術 卷一》-...
    阿呆變Geek閱讀 14,903評論 14 507
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,923評論 18 139
  • 記得前些年有個劉德華投資的小成本電影《瘋狂的石頭》,一部投資幾百萬的小制作電影,票房超過2000萬,并捧紅了后來的...
    未來十年新零售閱讀 152評論 0 1
  • 歲月流逝 你容顏老矣 曾經的輝煌業績 早載入歷史 留下時光痕跡 注入夢海追憶 ……
    六月天氣閱讀 299評論 33 27