退出Swing应用程序时偶尔会出现InterruptedException

时间:2010-05-20 12:03:07

标签: java concurrency multithreading java-2d interrupted-exception

我最近将计算机更新为功能更强大的计算机,配备了四核超线程处理器(i7),因此可以提供大量真正的并发性。现在,当我退出(System.exit(0))我正在开发的应用程序(使用Swing GUI)时,我偶尔

Exception while removing reference: java.lang.InterruptedException
java.lang.InterruptedException
        at java.lang.Object.wait(Native Method)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:134)
        at sun.java2d.Disposer.run(Disposer.java:125)
        at java.lang.Thread.run(Thread.java:619)

好吧,鉴于它开始发生在一个更具并发能力的硬件上,并且它与线程有关,并且它偶尔发生,它显然是某种时间性的东西。但问题是堆栈跟踪太短了。我只有上面的列表。它根本不包括我自己的代码,因此有点难以猜出错误的位置。

之前有没有人经历过这样的事情?任何想法如何开始解决它?

修改:退出使用System.exit(0)的Swing应用程序可能是“不干净”,但我不想将主框架设置为EXIT_ON_CLOSE,因为我想要确保应用程序退出时没有任何关键因素,我添加了一种机制,以便在调用dispose()之前执行主框架的System.exit(0)方法。所以它现在应该很干净,但偶尔也会发生异常。它发生在调用System.exit(0)之后; dispose()无问题。也就是说,它必须来自关闭钩子:

mainFrame.dispose(); // No problem! After this returns, all visible GUI is gone.
// In fact, if there were no other threads around, the VM could terminate here.
System.exit(0); // Throws an InterruptedException from sun.java2d.Disposer.run

我甚至尝试通过循环遍历Window数组(它包含无主Window.getWindows() s等)明确地处理所有Dialog,但它没有任何区别。这个问题似乎与“清洁度”(即在退出前明确释放本机屏幕资源)无关。这是别的什么,但是什么?

编辑2:将默认关闭操作设置为EXIT_ON_CLOSE没有任何区别。 http://www.google.com/search?q=sun.java2d.Disposer.run(Disposer.java:125)发现了一些错误报告,所以这可能确实是Sun的Java2D实现中的一个错误。我可以想象这样的虫子可以长时间不固定,因为它们在实践中非常无害;关闭钩子的例外几乎不会伤害任何其他人。鉴于这种情况发生在GUI应用程序中,除非将stderr定向到控制台或日志,否则甚至不会发现异常。

17 个答案:

答案 0 :(得分:15)

您的Disposer在调用remove()时被阻止(删除下一个平台本机资源)。这意味着当VM退出时,处理程序线程(守护程序线程)不会自然关闭(您应该期望通过System.exit()终止它)。

您的应用程序中有一个非守护程序线程,当您所有的摆动窗口都被丢弃时,它会阻止VM退出。

解决方案:找到它并导致它退出。

正常情况下,如果摆动应用程序的所有摆动窗口都已被处理掉,它将正常退出,例如,此程序将弹出一个窗口,然后一旦关闭就退出(所有这些都没有调用System.exit()):

public static void main(String args[]) throws Exception {
    JFrame jf = new JFrame();
    jf.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    jf.setVisible(true);
}

您也可以在退出之前尝试运行垃圾收集器,只是为了踢。

答案 1 :(得分:4)

  

如果您正在使用swing应用程序,则首先调用System.gc()然后调用dispose()方法。我认为它会正常工作..我也使用它。

想投票,但我需要更多代表。这个解决方案对我有用,虽然我找不到解释原因,而我的同事也说没有意义。

我有1.7和我创建的swing应用程序,它读入文件,重新排列内容然后输出到文件。有一个运行和退出按钮。使用首选项API,编写器,阅读器和其他一些东西。在打开和关闭应用程序(没有System.gc())时,只会连续两次连续返回如上所述的相同异常。但是在System.gc()之前dispose(),我无法再次抛出异常。

答案 2 :(得分:2)

我想知道它是否与这个错误无关:

Java Bug 6489540

基本上,这意味着如果在存在InheritableThreadLocal对象或存在自定义contextClassLoader时使用Java2D,它们将被捕获并永久存在,从而导致内存泄漏,并可能出现这些奇怪的锁。

如果是这种情况,则解决方法是在主线程上触发Java2D操作(即,在应用程序启动时立即执行),以便DisposerThread生活在“干净”的环境中。它是由一个静态初始化程序启动的,所以只需一个java2d资源的虚拟加载并释放它就足以得到一个不会挂在不需要的东西上的DisposerThread。

答案 3 :(得分:2)

System.exit()可能不是关闭基于Swing的应用程序的最简洁方法

无法将主框架设置为EXIT_ON_CLOSE:

mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE )

然后将其设置为隐形?

相关问题在这里; System.exit(0) in java

答案 4 :(得分:2)

我想我已经找到了bug的来源!

当你有一个带有Exit子菜单的基本java应用程序进入File菜单时...... 有一个快捷方式可以激活退出操作...此快捷方式必须创建一个循环链接或类似的东西......

有解决方案:

首先,这是可选的,以清楚地说明: 从主窗口实现此接口“WindowListener”。

在构建这个主窗口时,请执行以下操作:

JFrame frame=this.getFrame();
if(frame!=null)
{
   Window[] windows=frame.getWindows();
   for(Window window : windows)
   window.addWindowListener(this);
}

从接口实现函数:

public void windowOpened(WindowEvent e) {}

public void windowClosing(WindowEvent e) {
    JFrame frame=this.getFrame();
    if(frame!=null)
    {
        Window[] windows=frame.getOwnedWindows();
        for(Window window : windows)
        {
            window.removeWindowListener(this);
            window.dispose();
        }
    }
    //clear();
    System.gc();
}

public void windowClosed(WindowEvent e) {}
public void windowIconified(WindowEvent e) {}
public void windowDeiconified(WindowEvent e) {}
public void windowActivated(WindowEvent e) {}
public void windowDeactivated(WindowEvent e) {}

之后你必须小心你的快捷方式! 您必须从Exit菜单中删除Action才能使用actionPerformed:

进行管理
private void exitMenuItemActionPerformed(java.awt.event.ActionEvent evt) {
    //this.clear();
    svExitMenuItem.removeActionListener(null);//this call removes the end error from this way to exit.
    javax.swing.ActionMap actionMap = org.jdesktop.application.Application.getInstance(etscampide.ETScampIDEApp.class).getContext().getActionMap(ETScampIDEView.class, this);
    actionMap.get("quit").actionPerformed(null);//null to avoid end error too
}

我不解释原因,但它对我有用......我认为有一种循环引用...... 希望能帮到你。 见你。

答案 5 :(得分:2)

我有同样的问题(Java 6)。 我注意到调试器中有一个unknwon sun.awt.image.ImageFetcher线程。 我认为它来自于我使用JButtons with Icons。 ImageFetcher线程在2秒后消失。 如果ImageFetcher线程未运行,我的程序退出就好了。

答案 6 :(得分:1)

听起来你有一个运行的线程在你退出时没有终止。值得注意的是,该线程是wait()ing,如果您尝试在线程运行时停止该线程,则该方法会抛出一个中断的异常。我总是设置后台线程作为守护进程运行,这也可能有所帮助。

我会在你的JFrame中执行以下操作:

myJFrame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
  public void windowClosing(WindowEvent we) {
    // Some pieces of code to instruct any running threads, possibly including
    // this one, to break out of their loops

    // Use this instead of System.exit(0) - as long as other threads are daemons,
    // and you dispose of all windows, the JVM should terminate.
    dispose();
  }
});

通过手动断开循环,允许等待方法终止(希望你不会等待很长时间,是吗?如果你是,你可能想重新检查你如何利用你的线程)然后在你的代码中找到一个可以安全破解的点 - 安全因为你编写了这样做 - 然后线程将终止,让应用程序终止就好了。

<强>更新 也许你在某个地方不恰当地使用事件调度线程,当你试图退出时它正在等待/仍在为你工作?调度程序线程应该尽可能少地工作,并且应该尽可能快地将任何复杂的东西传递给另一个线程。我会承认我在黑暗中稍微刺了一下,但我倾向于认为这不是一个错误,特别是考虑到你开始在一台更强大的机器上注意到它 - 这让我尖叫“竞争条件!”不是Java bug。

答案 7 :(得分:1)

有时会再次出现错误,但这似乎工作正常:

private void exitMenuItemActionPerformed(java.awt.event.ActionEvent evt) {
    svExitMenuItem.removeActionListener(null);
    windowClosing(null);//i add it to clear and call the garbadge collector...
    javax.swing.ActionMap actionMap = org.jdesktop.application.Application.getInstance(etscampide.ETScampIDEApp.class).getContext().getActionMap(ETScampIDEView.class, this);
    actionMap.get("quit").actionPerformed(null);
}

答案 8 :(得分:1)

我遇到了同样的问题,发现我只是在后台隐藏了一个Frame(我把它的可见性设置为false)。如果我确保在我的隐藏帧上调用.dispose(),然后在我的mainFrame上调用.dispose(),我就不需要调用System.exit(0),应用程序只是自行清理并关闭。我的代码是Scala,但想法是一样的。

def top = new MainFrame {

   import javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE
   peer.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE)
   override def closeOperation() { endExecution; PreviewFrame.dispose(); this.dispose(); }
}

答案 9 :(得分:0)

试试这个。这对我有用。我正在创建在System.exit(0)调用后未正确处理的JFileChooser实例。

setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
             public void windowClosing(WindowEvent ev) {                	
                dispose();
                System.exit(0);
                }
 });

答案 10 :(得分:0)

我刚使用数据库遇到此错误。问题是没有关闭与数据库的连接。我通过使用以下代码解决了这个问题:

Runtime.getRuntime().addShutdownHook(new Thread() {
        public void run() {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    });

把它扔给其他人......

答案 11 :(得分:0)

这似乎是Java 1.7中解决的错误。

配置我的Eclipse在jdk 1.7中运行,错误就消失了。

约阿夫

答案 12 :(得分:0)

尝试使用EventQueue.invokeLater()关闭Swing。

答案 13 :(得分:0)

我遇到了类似的错误,但这对我有用:

private static void createAndShowGUI() {
  JFrame jf = new MyProgram();
  jf.setVisible(true);
}

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {

        public void run() {
          createAndShowGUI();
        }
    });
}

答案 14 :(得分:0)

java中的线程仅在所有run()方法完成执行时终止。否则,你将永远在VM中使用那些Thread扩展对象。您必须在退出应用程序之前完成所有线程。

还要考虑在创建jFrame时,您正在启动一个线程(我相信CURRENT_THREAD)

答案 15 :(得分:0)

如果您使用Swing App Framework,则可以覆盖Application.exit()以执行清理,或者添加ExitListener。否则,您还可以向应用添加关闭挂钩Runtime.getRuntime().addShutdownHook()

答案 16 :(得分:-1)

如果您正在使用swing应用程序,则首先调用System.gc()然后调用dispose()方法。 我认为它会正常工作..我也使用它。