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

时间:2014-07-20 11:18:55

标签: java swing jframe deadlock javax.sound.midi

在使用Swing界面将程序写入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())以致电new JFrame() ,那么它是有效的!为什么? solution0()是否合适,或者我是否幸运?这对我来说似乎是一个脆弱的解决方案。
  3. 为了更好的衡量标准,我还写了solution1()solution2(),两者似乎都没有挂起。这些版本是否比solution0()更正确,还是过度杀伤?将synth对象放在一个单独的线程中会使程序的其余部分难以使用它。

1 个答案:

答案 0 :(得分:4)

如果是线程,则无法保证首先运行哪个线程。

因此,我建议您在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);
    }
});