java - 如何将 SwingWorker 与 Swing 同步?

标签 java swing swingworker

问题:我希望在所有工作线程停止后关闭主应用程序窗口。这很容易。但我还想确保 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 大师,您建议我如何解决这个问题?

最佳答案

一种可能的解决方案:考虑使用 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

关于java - 如何将 SwingWorker 与 Swing 同步?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12545098/

相关文章:

java - JPA + SwingWorker : Query Error

java - 在远程机器上构建项目

java - 暂停和恢复每 n 秒发生一次的 SpriteBatch 动画的最佳方法

java - 在没有监听器的情况下获取鼠标位置

java - 框架内任何地方的 ListenKey 有更好的解决方案吗?

java - 重定向到新 URL 时小程序崩溃

java - JTable 中的选定单元格不刷新

java - 使用 Apache Tiles 3 和 Spring MVC 3.2.3 从 JSP 获取路径

java - DatePicker setEditable 为 false 仍然允许点击日历

java - 如何在java程序中从access数据库中检索整数列的最大值?