2012-09-22 33 views
2

問題:我想要在所有工作線程停止後關閉主應用程序窗口。這很容易。但我也想確保SwingWorker發送的消息將在關閉窗口發送的消息之前進行處理。 我在這裏有這個(並非如此)小例子源文件。如何將SwingWorker與Swing同步?

/***************************************************************************** 
    TestAsyncEvents.java 

This example shows that messages from SwingWorker threads get processed 
*AFTER* the WINDOW_CLOSED event even if they had been generated 
*BEFORE* them. 
The question is: HOW DO I MAKE IT RIGHT? 
I know the answer, but it doesn't satisfy me. ;) 
*****************************************************************************/ 
import java.util.List; 
import java.awt.Dimension; 
import java.awt.Toolkit; 
import java.awt.event.WindowAdapter; 
import java.awt.event.WindowEvent; 
import javax.swing.JFrame; 
import javax.swing.SwingUtilities; 
import javax.swing.SwingWorker; 

////////////////////////////////////////////////////////////////////////////// 
// 
public class TestAsyncEvents extends JFrame 
{ 

/* 
* This class is for closing main window 
*/ 
private class Closer extends WindowAdapter 
{ 
    @Override public void windowClosing(WindowEvent ev) { handleWindowClosing(); } 
    @Override public void windowClosed(WindowEvent ev) { handleWindowClosed(); } 
} 

/* 
* This class represents worker that gets asked to stop 
* and then reports when it actually stops 
*/ 
private class Ticker extends SwingWorker<Boolean,String> 
{ 
    private boolean stop_flag = false; 
    private int  counter = 0; 
    private boolean result; 
    Ticker() { super(); execute(); } 
    @Override protected Boolean doInBackground() 
    { 
     // body, executed in worker thread 
     try { 
      while(!stop_flag) 
      { 
       Thread.sleep(2000); 
       publish(String.format("Tick #%d",++counter)); 
      } 
      return result=true; 
     } catch(Exception ex) { 
      return result=false; 
     } 
    } 
    @Override protected void process(List<String> chunks) 
    { 
     // reports progress, executed in gui thread 
     for(String chunk: chunks) 
      System.out.println(String.format("Chunk processed: %s",chunk)); 
    } 
    @Override protected void done() 
    { 
     // reports result, executed in gui thread 
     System.out.println(String.format("Thread is %s.",isCancelled()?"cancelled":"stopped normally")); 
     System.out.println(String.format("Result is %s.",Boolean.toString(result))); 
     //postClosing(); // IT IS THE SOLUTION FOR MY PROBLEM! BUT... IT ISN'T GOOD ONE! 
    } 
    public void askToStop() { stop_flag = true; } 
} 

/****************************************************************************/ 
/* FIELDS                 */ 
/****************************************************************************/ 
private static TestAsyncEvents  self; 
private Ticker  worker_object = null; 

/****************************************************************************/ 
/* CONSTRUCTOR                */ 
/****************************************************************************/ 
TestAsyncEvents() 
{ 
    super("Testing Async Events"); 
    addWindowListener(new Closer()); 
    setMinimumSize(new Dimension(512,384)); 
    setVisible(true); 
    worker_object = new Ticker(); 
} 

/****************************************************************************/ 
/* INNER METHODS               */ 
/****************************************************************************/ 
/* 
* Waiting for worker to finish 
*/ 
private void doStopping() 
{ 
    worker_object.askToStop(); 
    while(!worker_object.isDone()); 
} 
private boolean stopInSeparateThread() 
{ 
    try { 
     Thread closer = new Thread(new Runnable(){public void run(){doStopping();}}); 
     closer.start(); 
     closer.join(); 
     return true; 
    } catch(Exception ex) { 
     return false; 
    } 
} 
private boolean stopHere() 
{ 
    doStopping(); 
    return true; 
} 
private boolean stopWorker() 
{ 
    //return stopInSeparateThread(); 
    return stopHere(); 
} 
private boolean canClose() 
{ 
    return worker_object.isDone(); 
} 
/* 
* Posting WM_CLOSE events 
*/ 
private void doPostCloseEvent() 
{ 
    Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(new WindowEvent(this,WindowEvent.WINDOW_CLOSING)); 
} 
private void postInSeparateThread() 
{ 
    SwingUtilities.invokeLater(new Runnable(){public void run(){doPostCloseEvent();}}); 
} 
private void postHere() 
{ 
    doPostCloseEvent(); 
} 
private void postClosing() 
{ 
    //postInSeparateThread(); 
    postHere(); 
} 
/* 
* Methods for Closer class 
*/ 
private void handleWindowClosing() 
{ 
    System.out.println("Closing main window..."); 
    if(canClose()) 
    { 
     System.out.println("Can close! Disposing..."); 
     dispose(); 
    } else { 
     System.out.println("Can't close! Now we'll allow it by stopping worker thread..."); 
     boolean res = stopWorker(); 
     System.out.println(String.format("Stopping worker thread went %s.",res?"okay":"wrong")); 
     postClosing(); // HERE I SIGNAL THE MAIN WINDOW TO CLOSE 
    } 
} 
private void handleWindowClosed() 
{ 
    System.out.println("Main window is closed!"); 
} 

/****************************************************************************/ 
/* ENTRY POINT                */ 
/****************************************************************************/ 
public static void main(final String[] args) 
{ 
    SwingUtilities.invokeLater(new Runnable(){public void run(){self=new TestAsyncEvents();}}); 
    System.out.println("All systems are go!"); 
} 

} 
////////////////////////////////////////////////////////////////////////////// 

其輸出是在這裏:

F:\C\Java\Test-Frame-Events>java TestAsyncEvents 
All systems are go! 
Chunk processed: Tick #1 
Closing main window... 
Can't close! Now we'll allow it by stopping worker thread... 
Stopping worker thread went okay. 
Closing main window... 
Can close! Disposing... 
Main window is closed! 
Chunk processed: Tick #2 
Thread is stopped normally. 
Result is true. 

我要的是在這裏:

F:\C\Java\Test-Frame-Events>java TestAsyncEvents 
All systems are go! 
Chunk processed: Tick #1 
Closing main window... 
Can't close! Now we'll allow it by stopping worker thread... 
Stopping worker thread went okay. 
Chunk processed: Tick #2 
Thread is stopped normally. 
Result is true. 
Closing main window... 
Can close! Disposing... 
Main window is closed! 

它從SwingWorker的事件在一個完全不同的事件隊列的處理髮生在我,不處理窗口消息等的那個。我故意等待工作線程停止併發布所有消息,然後再發布WINDOW_CLOSING事件。但它不起作用 - 來自SwingWorker的消息在WINDOW_CLOSING和WINDOW_CLOSED事件之後仍然得到處理。這會導致許多微小的不便。特別是,因爲我關閉了WINDOW_CLOSED處理程序中的所有日誌記錄,希望這些將成爲我的程序中執行的最後一個運算符,所以來自工作線程的所有消息在時間和空間上都會丟失。

我知道我的問題的解決方案。我必須取消註釋#68行和註釋行#161。但是這意味着如果我有多個SwingWorker線程,我應該產生另一個只關注所有工作者的線程,並在所有工作停止時發出主窗口退出信號。這只是不完整,恕我直言。 所以,Java大師,你如何建議我解決這個問題?

回答

2

一個可能的解決方案:可以考慮使用一個PropertyChangeListener:

private void handleWindowClosing() { 
    System.out.println("Closing main window..."); 
    if (canClose()) { 
    System.out.println("Can close! Disposing..."); 
    dispose(); 
    } else { 
    System.out 
      .println("Can't close! Now we'll allow it by stopping worker thread..."); 

    worker_object.addPropertyChangeListener(new PropertyChangeListener() { 

     @Override 
     public void propertyChange(PropertyChangeEvent pcEvt) { 
      if (SwingWorker.StateValue.DONE.equals(pcEvt.getNewValue())) { 
       postClosing(); 
      } 
     } 
    }); 


    boolean res = stopWorker(); 
    System.out.println(String.format("Stopping worker thread went %s.", 
      res ? "okay" : "wrong")); // !! 
    // postClosing(); // HERE I SIGNAL THE MAIN WINDOW TO CLOSE 
    } 
} 

如果您有多個工人,你可能使用CountDownLatchCyclicBarrier

+1

另見本相關[示例](HTTP://計算器的.com /一個/230513分之11372932)。 – trashgod

+0

謝謝,我會嘗試這些,看看什麼效果最好。 –