2011-05-11 59 views
0

我有一些代碼響應PropertyChangeEvent。這個事件的問題在於它可以連續發射幾次或根本不發射。這個事件是否僅僅被解僱一次或多次是不可預測的,而且我無法控制這個事件是如何被解僱的。我希望事件監聽器只被解僱一次。忽略重複發生的事件

我對這個問題的解決方案是使用我寫的名爲DelayedRunnable的類。我把聽衆打包在DelayedRunnable。當事件第一次被觸發時,偵聽器計劃在一秒鐘後執行 - 這是我任意決定的一段時間。隨後的事件發生將被忽略,直到這一秒鐘過去。下面是DelayedRunnable代碼:

public class DelayedRunnable implements Runnable { 
    final Runnable     runnable; 
    final int      delayAmount; 
    final TimeUnit     delayUnit; 
    final ScheduledExecutorService service   = new ScheduledThreadPoolExecutor(1); 
    final Runnable     executeRunnable = new ExecuteRunnable(); 
    final AtomicBoolean   scheduled  = new AtomicBoolean(false); 

    public DelayedRunnable(Runnable runnable, int delayAmount, TimeUnit delayUnit) { 
     if (runnable == null) 
      throw new IllegalArgumentException("runnable == null"); 
     this.runnable = runnable; 
     this.delayAmount = delayAmount; 
     this.delayUnit = delayUnit; 
    } 

    public void run() { 
     if (!scheduled.compareAndSet(false, true)) 
      return; 
     service.schedule(executeRunnable, delayAmount, delayUnit); 
    } 

    class ExecuteRunnable implements Runnable { 
     public void run() { 
      runnable.run(); 
      scheduled.set(false); 
     } 
    } 
} 

當我換聽者DelayedRunnable,它就會被調用一次。 DelayedRunnable的一個輔助好處是,偵聽器代碼不會在Swing EDT上執行,因爲偵聽器代碼很昂貴。

但是我發現有時聽者永遠不會被調用,可能是由於DelayedRunnable中的併發問題。當我重新啓動應用程序時,聽衆再次神奇地工作。正如人們所期望的那樣,由於併發編程的性質,我無法重現DelayedRunnable無法工作的情況。另外,一秒的時間限制完全是任意的。在某些電腦上,一秒鐘可能不夠。在其他電腦上,一秒鐘太長。沒有簡單的方法來設定時間限制。

我有兩個問題:

  • 有誰知道有DelayedRunnable,我無法看到的問題?

  • 有沒有比採用這種DelayedRunnable方法更好的處理這個問題的方法?

+0

請參閱[應用程序設計問題](http://www.oracle.com/technetwork/articles/javase/index-142890.html#5)。 – trashgod

+0

移動到答案。 – jtahlborn

+0

trashgod,我無法控制事件是如何觸發的。而且,問題不在於事件會被永遠來回發射,而只是事件被不必要地和不可預測地被解僱了一次以上。 –

回答

0

AWT事件監聽器將在其他監聽器之前執行,因此您可以先在那裏過濾事件。喜歡的東西:

something like Container c; 
. 
. 
. 
c.addAWTEventListener(new AWTEventListener() 
{ 
    public void eventDispatched(AWTEvent e) 
    { 
     if(! e instanceof YOUR_EVENT_TYPE) 
     return; 
     //code to filter here 
     e.consume() //to remove if not in time bounds 
    } 
}); 

看到http://download.oracle.com/javase/1.4.2/docs/api/java/awt/event/AWTEventListener.html爲使用

+0

'PropertyChangeEvent'不是AWTEvent的子類,所以我不明白這將如何幫助海報...但仍然是一個有趣的想法! –

+0

我同意Kevin K寫的東西。 –

+0

或換句話說:這個答案是錯誤的;-) – kleopatra

2

一件事你應該小心被做EDT以外的GUI工作。因爲實際的 runnable運行在一個新的線程上(在executor服務中),它不能直接執行任何gui工作。

您的某個runnable可能會掛起,因此任何將來的執行都會被阻止。

+0

有效點的+1。同意,可能的失敗原因是被包裹的可運行狀態正在掛起以防止'預定'標記被清除;沒有任何跡象表明GUI工作正在被包裝的可運行程序中完成,並且OP顯示他意識到這一點的跡象,但無論如何都是好的。 –

+0

GUI代碼被封裝在'SwingUtilities.invokeLater()'中,但是監聽器的主要任務是不更新GUI。目前還不清楚_why_ runnables是否掛起。 –

+0

也許嘗試添加一些跟蹤到您包裝的Runnables,看看他們卡住的地方。 –

0

正如jtahlborn所說,gui的工作應該在事件隊列上完成。確保任何gui的作品都被包裝在EventQueue.invokeAndWaitEventQueue.invokeLater中。實際上,根據生成事件的方式的不同,您可能可以避免完全使用EventQueue.invokeLater的延遲機制,因爲要調用的runnable放在隊列的末尾。如果在第一次回調運行之前將所有多個事件回調添加到隊列中,則可以確定使用invokeLater添加到隊列的可運行隊列不會看到任何後續事件。然後你可以使用一個類似於你已經在DelayedRunnable中做過的標誌來確保你只爲第一個事件添加一個runnable。

+0

如果我正確地理解這一點,你建議聽者設置啓動時會出現一些標誌,然後在完成標誌時將其刪除。如果在設置標誌時調用監聽器,監聽器將忽略該事件。問題在於,偵聽器可能在第二個事件觸發之前完成。 –

+2

你的GUI不知道也不聽Listener調用的任何線程(例如),無論如何閱讀http://download.oracle.com/javase/tutorial/uiswing/concurrency/index.html和關於SwingWorker的描述可能是你的回答,因爲Executor可以啓動SWingWorker並使用「Swing中的多線程」注意1)真正的線程數量 - 從Executor 2/Executor + SwingWorker限制在Top25 bug中http://bugs.sun.com/top25_bugs.do – mKorbel

+0

@Samad - 這個想法是使用第一個回調來設置標誌並使用invokeLater將一個Runnable添加到事件隊列的末尾,然後Runnable會爲事件進行實際處理並清除該標誌。這將跳過在第一次回調時已經在事件隊列中的任何後續回調。如果事件是以編程方式生成的,則可能是這種情況,但並非總是如此。絕對不適用於用戶觸發的事件。 –