Открытие 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.)
У меня три вопроса:
- Получив подсказку, я также попытался написать это как
hang1()
, но это не сработало. ПочемуSwingUtilities.invokeLater()
недостаточный? - Если я переставлю линии (см.
solution0()
) звонитьsynth.open()
послеnew JFrame()
тогда это работает! Зачем? Являетсяsolution0()
правильно, или мне просто везет? Это кажется мне надуманным решением. - Для хорошей меры я также написал
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);
}
});