取消文件打开对话框后出现的InterruptedException - 1.6.0_26 版本

10
以下是代码的输出结果:
java.vendor     Sun Microsystems Inc.
java.version    1.6.0_26
java.runtime.version    1.6.0_26-b03
sun.arch.data.model     32
os.name     Windows XP
os.version  5.1
os.arch     x86
Input selection cancelled by user.
Exception while removing reference: java.lang.InterruptedException
java.lang.InterruptedException
    at java.lang.Object.wait(Native Method)
    at java.lang.ref.ReferenceQueue.remove(Unknown Source)
    at java.lang.ref.ReferenceQueue.remove(Unknown Source)
    at sun.java2d.Disposer.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

下面的代码显示了我的机器上的异常。
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class GUI extends JPanel implements ActionListener {

    private final String newline = System.getProperty("line.separator");
    JButton openButton;
    JTextArea log;
    JFileChooser fc;

    public GUI() {
        super(new BorderLayout());

        log = new JTextArea(20,40);
        log.setMargin(new Insets(5,5,5,5));
        log.setEditable(false);

        fc = new JFileChooser();

        openButton = new JButton("Open");
        openButton.addActionListener(this);

        JPanel buttonPanel = new JPanel(); //use FlowLayout
        buttonPanel.add(openButton);

        add(buttonPanel, BorderLayout.NORTH);
        add(new JScrollPane(log));

        showProp("java.vendor");
        showProp("java.version");
        showProp("java.runtime.version");
        showProp("sun.arch.data.model");
        showProp("os.name");
        showProp("os.version");
        showProp("os.arch");
    }

    public void showProp(String name) {
        output(name + " \t" + System.getProperty(name));
    }

    public void output(String msg) {
        log.append(msg + newline);
        log.setCaretPosition(log.getDocument().getLength());
        System.out.println(msg);
    }

    public void actionPerformed(ActionEvent e) {
        //Handle open button action.
        int returnVal = fc.showOpenDialog(GUI.this);

        if (returnVal == JFileChooser.APPROVE_OPTION) {
            //This is where a real application would open the file.
            output(
                "Input File Selected: " +
                fc.getSelectedFile().getName() +
                ".");

        } else {
            output("Input selection cancelled by user.");
        }
        log.setCaretPosition(log.getDocument().getLength());
    }

    /**
     * Create the GUI and show it.  For thread safety,
     * this method should be invoked from the
     * event dispatch thread.
     */
    private static void createAndShowGUI() {
        //Create and set up the window.
        JFrame frame = new JFrame("IDE Output Converter");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Add content to the window.
        frame.add(new GUI());

        //Display the window.
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        //Schedule a job for the event dispatch thread:
        //creating and showing this application's GUI.
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
}

当我运行程序时,主窗口正常打开,程序工作正常。

但是,如果你:

  • 使用“打开文件”按钮打开JFileChooser
  • 点击取消,然后
  • 退出程序

会抛出InterruptedException。或者,如果你选择一个文件并“打开”,然后退出程序,也会抛出同样的错误。在这个博客中,使用示例代码解释了同样的问题,他的解决方法是尽早调用new JFileChooser();,我已经这么做了但没有效果。

这是1.6.0_26版本中的bug吗?如果是,那么有没有对该版本的解决方法?

是代码的问题吗?如果是,怎么修复它?(看起来不太可能,因为另外两个空结果之一现在已被删除。)


抱歉...我用你的代码做了同样的事情,但是我没有得到任何异常。这段代码在所有情况下都能正常运行。 - Sumit Singh
也许是我的环境问题。我正在使用Eclipse Indigo Service Release 1和JRE6。 - mhollander38
我使用的是除了Java6之外的相同版本,我正在使用Java 7。 我已经更改了您的代码,甚至包括您的注释...只需粘贴并运行... 可能是环境出了问题... - Sumit Singh
刚刚又试了一下,你必须点击“打开文件”,在选择器中随意选择一个文件,然后取消并退出,然后错误就会出现。尽管即使这也已经证明是间歇性的。 - mhollander38
是的...我试过了。我尝试了20次,使用了所有可能的情况...但我还是没找到问题所在。 只有当线程处于睡眠状态并且在该线程上运行interrupt()方法时,才会出现Interrupted Exception。 - Sumit Singh
显示剩余10条评论
5个回答

12

我认为这是sun.awt.Disposer中的一个小错误。

该类创建了“Java2D Disposer”守护线程,用于处理垃圾回收对象(主要是AWT窗口)的AWT资源清理。大多数情况下,该线程在其引用队列上等待新的可清除对象进行垃圾回收。当线程被中断时,它会显式地打印该异常。

当JVM终止时,它会中断所有线程。在某些情况下-这些情况明显受到JFileChooser的使用及其初始化的子系统的影响-一些线程在此中断后仍有机会运行。在这种情况下,“Java2D Disposer”线程会抛出InterruptedException,因为它正在等待锁定。最好在关闭期间忽略该异常。

作为解决方法,请替换

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

使用

frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
    @Override
    public void windowClosed(WindowEvent e) {
        PrintStream nullStream = new PrintStream(new OutputStream() {
            public void write(int b) throws IOException {
            }

            public void write(byte b[]) throws IOException {
            }

            public void write(byte b[], int off, int len) throws IOException {
            }
        });
        System.setErr(nullStream);
        System.setOut(nullStream);
        System.exit(0);
    }
});

我基本明白你的意思,看起来是这个异常的原因。我已按照您的建议进行了更新,但仍然出现以下异常:在删除引用时发生异常:java.lang.InterruptedException java.lang.InterruptedException at java.lang.Object.wait(Native Method) at java.lang.ref.ReferenceQueue.remove(Unknown Source) at java.lang.ref.ReferenceQueue.remove(Unknown Source) at sun.java2d.Disposer.run(Unknown Source) at java.lang.Thread.run(Unknown Source) - mhollander38
抱歉,异常已经被打印出来了,而不是传播出去。我已经更改了代码片段,请再试一次。 - Ingo Kegel
点赞。检查了OpenJDK代码,你的分析很棒。 - MarianP
很遗憾,OP似乎已经消失了。我想听一下这段代码在问题机器上的运行报告。 - Andrew Thompson
是的,确认异常不再打印出来会很好。然而,我的回答更多是解释而不是解决方案,代码片段或多或少是一个隐藏异常的hack。 - Ingo Kegel
+200。我觉得这是在悬赏期限内(还剩5小时)提供的最好的答案(并且没有被删除)。由于提问者消失,问题实际上并没有得到解决/回答。:( - Andrew Thompson

4

我曾经遇到了类似的问题。我按照这个讨论串中的建议修复了它。


那里接受的答案是:“您的Disposer在调用remove()(删除下一个平台本地资源)时被阻塞。这意味着当VM退出时,处理器线程(守护线程)不会自然关闭”,这是错误的。处理器线程正在等待其引用队列,这是其默认状态。 - Ingo Kegel

2

我对稍微修改过的您的源代码进行输出(现已包含在问题本身的编辑中),结果为..

java.vendor     Sun Microsystems Inc.
java.version    1.6.0_29
java.runtime.version    1.6.0_29-b11
sun.arch.data.model     32
os.name         Windows 7
os.version      6.1
os.arch         x86
Input File Selected: install.ini.
Input selection cancelled by user.
Press any key to continue . . .

从您的问题中看来,重要的句子是:

Input selection cancelled by user.

但是在此之后,我在输出中没有看到任何InterruptedException

我运行了你的代码,但仍然出现以下异常:移除引用时出现 java.lang.InterruptedException 异常。 java.lang.InterruptedException at java.lang.Object.wait(Native Method) at java.lang.ref.ReferenceQueue.remove(Unknown Source) at java.lang.ref.ReferenceQueue.remove(Unknown Source) at sun.java2d.Disposer.run(Unknown Source) at java.lang.Thread.run(Unknown Source) - mhollander38
将源代码的确切输出作为问题的编辑添加到问题中。我已经将我的源代码作为编辑添加到您的问题中,因为它更具信息性,也能为您展示问题(是问题的一个示例)。 - Andrew Thompson

0

我在博客文章中看到的代码与您的代码之间的区别是JFileChooser的作用域。在博客文章中,该对象是onClickedButton函数内的局部变量。在您的示例中,该对象在类级别上定义。我认为作为局部变量会给Disposer线程更多时间来清除JFileChooser对象。

当我将文件选择器对象设置为您的示例中actionPerformed方法块中的局部变量时,异常未发生。我进行了大约十次测试,无论是通过eclipse还是通过命令行运行应用程序,都没有发生异常。

如果仍然出现异常,您可以在GUI的构造函数中初始化文件选择器对象,但不要将其分配给任何内容。我认为这里充当了一个重量级swing对象的早期初始化和处理。

希望这有所帮助。


0
尝试在使用JFileChooser后添加System.gc()调用。我曾经遇到过文件锁问题,这个调用解决了这个问题。

1
那是巫术,而不是解决方案。无法预测 System.gc() 会做什么。 - JimmyB
嗯,不是巫术,而是一个存在已久的Java错误#4724038。 - styken

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接