国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

J2SE5.0用Executor靈活處理事件下發

2019-11-18 11:54:46
字體:
來源:轉載
供稿:網友

  每個java開發人員都熟悉異步事件下發的EventListener模式。許多人也寫過用來治理偵聽器和下發事件給其他組件的樣板代碼。偵聽器是簡單、通用、靈活和輕易實現的,但他涉及到其他開發人員寫的代碼,這可能引起問題:
  
  1、一個低效的偵聽器可能花費太長的時間來處理事件,這使得其他偵聽器等待并可能引起死鎖。
  
  2、在你派發事件給偵聽器器的時候,你可以控制下發的線程。但通常實現偵聽器的開發人員不能控制事件如何被下發到他們的代碼。
  
  3、在一個多媒體應用中,如何同步GUI事件(如動畫和用戶交互)和其他異步事件流(如語音、聲音和視頻)不那么清楚。
  
  這篇文章使用Doug Lea的Executor(現在是J2SE 5.0的一部分)使得事件派發更加靈活。你可以使用這種思想在以下方面:
  
  1、答應開發人員使用你的組件作為事件派發策略的插件,這樣就可以自定義事件的派發方式。
  
  2、使不同的偵聽器相互獨立,因此一個低效的偵聽器就不會影響其他的了。
  
  3、多元化來自不同異步資源的事件流。
  
  JAVA1.4兼容性注重:這篇文章使用J2SE 5.0的泛型來排除事件和偵聽器器的轉換。Dispatchable Event Library也包含一個非泛型的版本可以運行在JDK 1.4上。較早的JDK缺少內建的java.util.concurrent包,但你可以下載兼容的后續支持版本。
  
  標準偵聽器
  
  在本文示例中,會編寫一個使用ClipEven類也報告視頻片斷事件(如啟動和暫停等)和實現ClipListener接口響應事件的視頻編輯系統。
  
  import java.util.EventListener;
  class ClipEvent extends EventObject {
  //...}public interface ClipListener extends EventListener {
  public void clipUpdate(ClipEvent e);}
  
  在許多程序中,事件是由一些控制類來創建,他們不僅負責維護相應的偵聽器而且派發每一個事件給相應的偵聽器。通常,我們可以分離這職責并且將偵聽器的維護代理給一個簡單的助手類dispatcher。ClipEventDispatcher示例了這種事件派發的方式:
  
  import java.util.*;public class ClipEventDispatcher {
  final Collection listeners = new ArrayList();
  public synchronized void
  addListener(ClipListener l) {
  listeners.add(l);
  }
  public synchronized void
  removeListener(ClipListener l) {
  listeners.remove(l);
  }
  public void fireEvent(ClipEvent e) {
  for(Iterator i = copyListeners();
  i.hasNext();) {
  ClipListener l = (ClipListener) i.next();
  l.clipUpdate(e);
  }
  } PRivate synchronized Iterator copyListeners() {
  return new ArrayList(listeners).iterator();
  }
  }
  
  ClipEventDispatcher暴露出如前面所討論的典型的下發問題。假如任何一個ClipListener有較慢的clipUpdate方法實現就會導致其他偵聽器等待。派發器的作者決定哪一個線程調用fireEvent方法,而ClipListener的開發人員卻沒有辦法自定義事件的下發。
  
  JAVA中靈活的任務執行:Executor接口
  
  J2SE 5.0標準化了java.util.concurrent包,包含來自Doug Lea創建的Executor接口。Executor運行實現了java.lang.Runnable接口的任務。
  
  class MyCoolTask implements Runnable { public void run() {
  //... do useful stuff }}Thread t = new Thread(new MyCoolTask());t.start();
  
  在Executor使用Runnables是類似的:
  
  Executor e = ...e.execute(new MyCoolTask());
  
  轉遞給execute方法的Runnable任務包含被Executor調用的run方法。但不像Thread只可以調用啟動方法一次,Executors可以運行許多Runnable任務。不同的Executors體現不同的執行任務的策略。例如,J2SE 5.0提供一個Executor作為調度器,這意味著他可以按照配置的時間周期性地運行任務。在下一頁的Useful Executors部分具體描述了幾種不同的Executor,但首先我們來看一下如何用他們來解決事件派發問題。
  
  用DispatchableEvent增加靈活性
  
  為了使組合Executor和事件更輕易,我開發了一個Dispatchable Event Library,他提供了助手類DispatchableEventSupport(用來維護偵聽器和事件派發)。在內部,DispatchableEventSupport實例使用一個Executor來觸發事件,因此可以改變Executor來自定義事件下發策略。
  
  下面是一個使用DispatchableEvent類庫來重寫的ClipEventDispatcher示例:
  
  import org.recoil.pixel.dispatchable.*;import org.recoil.pixel.executor.*;public class ClipEventDispatcher {  Executor e = new DirectExecutor(); //[1] DispatchableEventSupport<ClipListener> d =
  new DispatchableEventSupport<ClipListener>(e);
  public void addListener(ClipListener l) {
  d.addListener(l);
  }
  public void removeListener(ClipListener l) {
  d.removeListener(l);
  }
  public void fireEvent(ClipEvent e) {
  d.fireEvent(new DispatchableEvent
  <ClipListener, ClipEvent>(e) {
  public void
  dispatch( ClipListener l, ClipEvent ce) {
  l.clipUpdate(ce); //[2]
  }
  });
  }}
  
  在行[1]上我們使用DirectExecutor來簡化重建原始的ClipEventDispatcher行為。事件下發可以通過變化使用的Executor來自定義,或者在DispatchableEventSupport被創建時或者在偵聽器增加時。
  
  在行[2]上你只需要如此簡單的代碼來集成到你的應用中。Dispatchable Event Library處理了事件下發的機制,通常你所需要做的只是調用的回調函數(如clipUpdate)
  
  Dispatchable Event Library詳解
  
  Dispatchable Event Library包含幾個有用的助手類來派發任何類型的事件。要害的幾個類在org.recoil.pixel.dispatchable包中:
  
  DispatchableEventDispatcher:使用Executor觸發事件,但不提供偵聽器的維護。在你想為現有的事件派發代碼增加靈活性是非常有用。
  
  DispatchableEventSupport:大部分應用想要使用這個助手類,他為DispatchableEventDispatcher增加了偵聽器維護。假如你了解java.beans.PropertyChangeSupport你會覺得他也很熟悉。
  
  PropertyChangeEventDispatcher:組合了DispatchableEventDispatcher
  
  和PropertyChangeSupport,為PropertyChangeEvents提供了靈活的派發策略。這也是一個研究如何將DispatchableEvents與現有代碼集成的好例子。
  
  DispatchableEvent:用來擴展你的事件下發代碼的抽象類。
  
  有用的Executors
  
  Dispatchable Event Library的力量來自可以被用來自定義事件下發的可用Executors。下面我來看一下可用的Executors組:
  
  Dispatchable Event Library包含org.recoil.pixel.executor包:
  
  DirectExecutor:DirectExecutor在同一線程內同步調用提供給他的代碼。假如和DispatchableEventSupport一起使用這個類,你可以得到通用的偵聽器行為,這也是一個有用的缺省值。
  
  AWTExecutor:AWTExecutor在AWT事件派發線程的調度代碼。事件與AWTEvents交互。因此,由這個Executor調用的偵聽器可以自由地調用更新AWT和Swing GUI組件的方法而不需要使用SwingUtilities.invokeLater(),因為他們已經在正確的線程中被子調用。
  
  MIDPExecutor:MIDPExecutor在J2ME MIDlet中與AWTExecutor一致。他確保你的事件通過需要與MIDlet's GUI交互的callSerially方法下發。
  
  例如,為了在AWT事件派發線程中使用AWTExecutor來下發ClipEvents:
  
  import org.recoil.pixel.dispatchable.*;
  import org.recoil.pixel.executors.*;
  Executor e = new AWTExecutor();DispatchableEventSupport<ClipListener> d =new
  DispatchableEventSupport<ClipListener>(e);
  
  J2SE5.0內建的Executor
  
  新的J2SE 5.0類java.util.concurrent.Executors被用來創建復雜的線程池。你可以在池中配置線程數量,設置延遲或者周期調度。
  
  例如,使用J2SE 5.0 Executor提供一個容納5個事件下發線程的線程池
  
  import org.recoil.pixel.dispatchable.*;
  import java.util.concurrent.*;
  Executor tp = Executors.newFixedThreadPool(5);
  DispatchableEventSupport<ClipListener> d = new
  DispatchableEventSupport<ClipListener>(tp);
  
  J2EE并沒有提供標準的線程池功能,但Executor可以通過JMS或者消息BEAN來實現提供可配置的事件下發。
  
  問題解決
  
  現在我們已經看到Dispatchable Event Library和一些Executors,我們可以看一上如何使用這些工具來避免常見的偵聽器問題。
  
  避免等待
  
  DispatchableEventSupport提供2個addListener方法來避免偵聽器等待問題:
  
  public void addListener(L listener); public void addListener(L listener,Executor executor);
  
  addListener(L listener)方法在DispatchableEventSupport被創建的時候共享默認的Executor集合。而addListener(L listener, Executor executor)方法關聯自定義的Executor。
  
  這種方式不僅為組件的使用者提供了自定義事件下發的一種好的方式,而且幫助他們通過只有2個參數的addListener方法來分離偵聽器。
  
  import org.recoil.pixel.dispatchable.*;public class SharedComponent {
  DispatchableEventSupport<ClipListener> d =
  new DispatchableEventSupport<ClipListener>();
  public void
  addListener(ClipListener l, Executor e) {
  d.addListener(l, e);
  }
  public void fireEvent(ClipEvent e) {
  d.fireEvent(new DispatchableEvent
  <ClipListener, ClipEvent>(e) {
  public void
  dispatch( ClipListener l, ClipEvent ce) {
  l.clipUpdate(ce);
  }
  });
  }
  [...]}
  
  給SharedComponent增加偵聽器的開發人員被強制為每一個偵聽器定義一個Executor。假設每一個開發人員保持Executor為私有的,那么他的偵聽器就有一定的分離量。這在他們使用基于線程池的Executor時非常有用。
  
  假如所有相關的代碼都在團隊的控制下,那么SharedComponent是足夠的,但這還不能完全解決等待問題。假如你因為使用遺留的或第三方代碼而必須支持低效的偵聽器時,你可以通過控制和強制每一個偵聽器擁有自己的Executor來增加相互的獨立性。
  
  import org.recoil.pixel.dispatchable.*;
  import java.util.concurrent.*;
  public class DefensiveComponent {
  private final
  DispatchableEventSupport<ClipListener> d =
  new DispatchableEventSupport<ClipListener>();
  public void addListener(ClipListener l) {
  Executor e=Executors.newSingleThreadExecutor();
  d.addListener(l, e);
  }
  public void removeListener(ClipListener l) {
  d.removeFirstInstanceOfListener(l);
  }
  public void fireEvent(ClipEvent e) {
  d.fireEvent(new DispatchableEvent
  <ClipListener, ClipEvent>(e) {
  public void
  dispatch( ClipListener l, ClipEvent ce) {
  l.clipUpdate(ce);
  }
  });
  }
  [...]}
  
  DefensiveComponent為每一個增加的偵聽器附加對應的事件下發線程,這就分離了低效的偵聽器并且確保偵聽器可以被獨立的處理;高效的偵聽器不需要等待低效的。這種策略是簡單而安全的,但也是高代價的,因為他必須創建和銷毀許多線程。在大部分情況下,需要通過Executors創建合理大小的線程池也平衡獨立性和代價。
  
  同步多事件流
  
  DispatchableEvent答應你通過一個簡單的Executor多元化相應事件來同步來自不同異步資源的事件。
  
  例如,考慮一個支持鼠標和語音識別的多模畫板應用。
  
  通常語音識別在一斷語音被識別時派發一個事件。想像用戶選擇一個圖形然后說“刪除”。顯然我們希望鼠標事件被首先處理,否則可能會刪除錯誤的對象。一種簡單地解決這個問題的方法是使用AWTExecutor來下發語音事件,他會在事件被收到時將其放在AWT事件隊列中,確保首先處理MouseEvents。
  
  這個想法可以擴展到更多的異步事件流,通過將每一個事件源作為引用放到一個共享的基于隊列的Executor中。每一個事件根據順序放在隊列中,交叉地下發。
  
  小結
  
  這篇文章專注于可能發生在偵聽器范例中的問題。我們看到一個簡單的派發類庫如何通過Executors被用來自定義事件下發。使用不同的策略你可以將你的組件與子系統(如AWT)集成,你可以通過答應客戶定義使用的Executor來給予他們更多的選擇,或者你可以從另一方面來分離低效的偵聽器來防止等待。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 宿州市| 宽城| 城市| 澄江县| 叶城县| 凤山县| 哈密市| 崇左市| 安龙县| 双柏县| 庆安县| 濮阳县| 汤阴县| 焉耆| 靖远县| 思南县| 崇文区| 库伦旗| SHOW| 扎兰屯市| 镇宁| 利川市| 古浪县| 高清| 辽宁省| 鄂尔多斯市| 镇巴县| 黄浦区| 江山市| 烟台市| 云梦县| 巴青县| 和平县| 常宁市| 鄂伦春自治旗| 元江| 吕梁市| 清新县| 陇川县| 石嘴山市| 迁西县|