java - 在创建 JFrame 之前打开 MIDI Synth 会导致 JVM 挂起

标签 java swing jframe deadlock javax.sound.midi

在使用 Swing 接口(interface)编写 MIDI 程序时,我遇到了挂起,因此需要 kill -9。通过将以下程序作为 java MidiSwingProblem hang0

运行,它可以 100% 重现
import java.lang.reflect.InvocationTargetException;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Synthesizer;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class MidiSwingProblem {
    /**
     * JFrame never appears.  Hangs such that `kill -9` is required.
     */
    public static void hang0() throws MidiUnavailableException {
        Synthesizer synth = MidiSystem.getSynthesizer();
        synth.open();
        JFrame frame = new JFrame("MIDI Swing Hang 1");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    /**
     * JFrame never appears.  Hangs such that `kill -9` is required.
     */
    public static void hang1() throws MidiUnavailableException {
        Synthesizer synth = MidiSystem.getSynthesizer();
        synth.open();
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame frame = new JFrame("MIDI Swing Hang 2");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setVisible(true);
            }
        });
    }

    public static void solution0() throws MidiUnavailableException {
        // It doesn't matter whether .getSynthesizer() or new JFrame() is
        // called first.  It seems to work as long as synth.open() happens
        // after new JFrame().
        Synthesizer synth = MidiSystem.getSynthesizer();
        JFrame frame = new JFrame("MIDI Swing Solution 0?");
        synth.open();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    public static void solution1() {
        new Thread() {
            public void run() {
                try {
                    Synthesizer synth = MidiSystem.getSynthesizer();
                    synth.open();
                } catch (MidiUnavailableException noMidi) {
                    noMidi.printStackTrace();
                }
            }
        }.start();

        JFrame frame = new JFrame("MIDI Swing Solution 1?");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    public static void solution2() {
        new Thread() {
            public void run() {
                try {
                    Synthesizer synth = MidiSystem.getSynthesizer();
                    synth.open();
                } catch (MidiUnavailableException noMidi) {
                    noMidi.printStackTrace();
                }
            }
        }.start();

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame frame = new JFrame("MIDI Swing Solution 2?");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setVisible(true);
            }
        });
    }

    public static void main(String[] args) throws NoSuchMethodException
                                                , IllegalAccessException
                                                , InvocationTargetException {
        MidiSwingProblem.class.getMethod(args[0], new Class[0]).invoke(null);
    }
}

我假设 hang0() 中存在死锁,这是我的错而不是 J2SE 中的错误。 (我已经在 OS X 上验证了 Java 1.7 和 1.8 的行为。)

我有三个问题:

  1. 参加 hint ,我也试过把它写成hang1(),但是没用。为什么 SwingUtilities.invokeLater() 不够用?
  2. 如果我重新排列行(参见 solution0())以调用 synth.open() after new JFrame() ,然后就可以了!为什么? solution0() 是否正确,或者我只是走运?对我来说,这似乎是一个脆弱的解决方案。
  3. 为了更好地衡量,我还编写了 solution1()solution2(),它们似乎都没有挂起。这些版本是否比 solution0() 更正确,或者它们是否矫枉过正?将 synth 对象放在单独的线程中会使程序的其余部分很难使用它。

最佳答案

在线程的情况下,不能保证哪个线程会先运行。

所以我建议你把代码写在SwingUtilities.invokeLater()中或 EventQueue.invokeLater()确保 EDT正确初始化。

阅读更多

示例代码:

SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        try {
            Synthesizer synth = MidiSystem.getSynthesizer();
            synth.open();
        } catch (MidiUnavailableException e) {
            e.printStackTrace();
        }

        JFrame frame = new JFrame("MIDI Swing Solution 1?");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
});

关于java - 在创建 JFrame 之前打开 MIDI Synth 会导致 JVM 挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24849966/

相关文章:

java - 正则表达式如何获取/之后的所有内容

java - 如何更改 CTabItem 下划线的颜色?

java - JSTL & Spring : Accessing methods with arguments

java - Java中如何比较日期?

java - JFrame中的图像显示

java - 为什么我的代码不能同时运行两个面板?当我运行它时它没有显示任何东西,但它编译

Java:从新的 JFrame 获取复选框名称

java - ListSelectionListener 双击

java - JLabel 更改时 Applet 重新定位

java - JButton Action 在另一个类中执行