在使用 Swing 接口向 MIDI 编写程序时,我遇到了挂起,这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 中的错误。(我已经在 OS X 上验证了 Java 1.7 和 1.8 的行为。)
我有三个问题:
- 提示一下,我也尝试将其写为
hang1()
,但这没有用。为什么SwingUtilities.invokeLater()
不足? - 如果我重新排列线路(参见
solution0()
)以synth.open()
在 之后new JFrame()
调用,那么它可以工作!为什么?是solution0()
正确的,还是我只是走运?对我来说,这似乎是一个脆弱的解决方案。 - 为了更好地衡量,我还写了
solution1()
andsolution2()
,两者似乎都没有挂起。这些版本是比 更正确solution0()
,还是矫枉过正?将synth
对象放在单独的线程中会使程序的其余部分难以使用它。