2012-09-22 24 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

谢谢,我会尝试这些,看看什么效果最好。 –