Открытие MIDI Synth перед созданием JFrame приводит к зависанию JVM

При написании программы для MIDI с интерфейсом Swing у меня возникло зависание, так что kill -9 необходимо. Воспроизводится на 100%, если запустить следующую программу как java MidiSwingProblem hang0

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. (Я проверил поведение на Java 1.7 и 1.8 на OS X.)

У меня три вопроса:

  1. Получив подсказку, я также попытался написать это как hang1(), но это не сработало. Почему SwingUtilities.invokeLater() недостаточный?
  2. Если я переставлю линии (см. solution0()) звонить synth.open() после new JFrame()тогда это работает! Зачем? Является solution0() правильно, или мне просто везет? Это кажется мне надуманным решением.
  3. Для хорошей меры я также написал solution1() а также solution2()Оба из которых, кажется, не висят. Являются ли эти версии более правильными, чем solution0()Или они излишни? Имея synth Объект в отдельном потоке затрудняет использование остальной частью программы.

1 ответ

В случае потока нет никакой гарантии, что какой поток будет запущен первым.

Поэтому я предлагаю вам написать код в 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);
    }
});
Другие вопросы по тегам