在使用 Swing 接口(interface)编写 MIDI 程序时,我遇到了挂起,因此需要 kill -9
。通过将以下程序作为 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 的行为。)
我有三个问题:
- 参加 hint ,我也试过把它写成
hang1()
,但是没用。为什么SwingUtilities.invokeLater()
不够用? - 如果我重新排列行(参见
solution0()
)以调用synth.open()
afternew JFrame()
,然后就可以了!为什么?solution0()
是否正确,或者我只是走运?对我来说,这似乎是一个脆弱的解决方案。 - 为了更好地衡量,我还编写了
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/