问题:我想在所有工作线程停止后关闭主应用程序窗口。这很简单。但我还想确保SwingWorker发送的消息将在关闭窗口发送的消息之前处理。 我有这个(不是那么)小例子源文件。
/*****************************************************************************
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大师,您如何建议我解决这个问题?
答案 0 :(得分: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
}
}
如果您有多名工作人员,则可以使用CountDownLatch或CyclicBarrier