【问题标题】:Freezing in JVM7 but not in JVM6在 JVM7 中冻结,但在 JVM6 中不冻结
【发布时间】:2013-10-06 02:13:12
【问题描述】:

一位朋友最近让我制作一个简单的 Buzzer 程序,并在我为他写的内容中发现了一个奇怪的“错误”。

如果按下一个键并且蜂鸣器在几秒钟内快速复位,他会观察到在复位后的第一次按键和“蜂鸣”指示之间发生 2 到 3 秒的程序冻结。他有以下 Java 安装:

build 1.7.0_25-b16

但是,我的计算机上没有遇到此问题,安装了以下 Java:

$ java -version
java version "1.6.0_51"
Java(TM) SE Runtime Environment (build 1.6.0_51-b11-457-10M4509)
Java HotSpot(TM) 64-Bit Server VM (build 20.51-b01-457, mixed mode)

解冻后,程序会返回相应的键(也就是说,不是最后按下的键,而是在重置之后和冻结之前按下的第一个键)。这表明问题不在于听者,而在于听者的反应。

对可能导致这种现象的原因有什么想法吗?提前感谢您的帮助。

源代码:

/**
 * Buzzer.java
 *
 * Buzzer
 */
package org.lexingtonma.lhs.nhb;

import java.awt.Color;
import java.awt.DefaultKeyboardFocusManager;
import java.awt.Font;
import java.awt.KeyEventDispatcher;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;


/**
 * @author Arman D. Bilge
 *
 */
public class Buzzer extends JFrame implements KeyListener {

    private static final long serialVersionUID = 7492374642744742658L;
    private static final String BUZZ_A = "BuzzA.wav";
    private static final String BUZZ_B = "BuzzB.wav";
    private Clip buzz = null;
    private boolean listening = true;
    private final JTextField display = new JTextField(3);
    private final JButton reset = new JButton("Reset");

    {

        DefaultKeyboardFocusManager.getCurrentKeyboardFocusManager()
            .addKeyEventDispatcher(new KeyEventDispatcher() {
                public boolean dispatchKeyEvent(KeyEvent e) {
                    keyTyped(e);
                    return false;
                }
            });
        setTitle("Buzzer");
        final JPanel panel = new JPanel();
        setSize(256, 162);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        display.setFont(new Font("Helvetica", Font.BOLD, 64));
        display.setForeground(Color.WHITE);
        display.setVisible(true);
        display.setEditable(false);
        display.setHorizontalAlignment(JTextField.CENTER);
        panel.add(display);
        reset.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                display.setText("");
                display.setBackground(Color.WHITE);
                listening = true;
                reset.setEnabled(false);
            }
        });
        reset.setEnabled(false);
        panel.add(reset);
        add(panel);
        try {
            buzz = AudioSystem.getClip();
        } catch (LineUnavailableException e) {
            JOptionPane.showMessageDialog(this, e.getLocalizedMessage(), "FATAL ERROR", JOptionPane.ERROR_MESSAGE);
        }
    }

    public static final void main(String args[]) {
        Buzzer b = new Buzzer();
        b.setVisible(true);
    }

    public void keyPressed(KeyEvent e) {
        // Do nothing        
    }

    public void keyReleased(KeyEvent e) {
        // Do nothing        
    }

    public void keyTyped(KeyEvent e) {
        final char c = e.getKeyChar();
        if (listening && Character.isLetterOrDigit(c)) {
            buzz.close();
            listening = false;
            if (Character.isDigit(c)) {
                display.setBackground(Color.RED);
                try {
                    buzz.open(AudioSystem.getAudioInputStream(getClass().getResource(BUZZ_A)));
                    buzz.start();
                } catch (Exception ex) {
                    JOptionPane.showMessageDialog(this, ex.getLocalizedMessage(), "FATAL ERROR", JOptionPane.ERROR_MESSAGE);
                    System.exit(1);
                }
            } else {
                display.setBackground(Color.BLUE);
                try {
                    buzz.open(AudioSystem.getAudioInputStream(getClass().getResource(BUZZ_B)));
                    buzz.start();
                } catch (Exception ex) {
                    JOptionPane.showMessageDialog(this, ex.getLocalizedMessage(), "FATAL ERROR", JOptionPane.ERROR_MESSAGE);
                    System.exit(1);
                }
            }
            display.setText("" + c);
            reset.setEnabled(true);
        }

    }

}

罐子:https://www.dropbox.com/s/62fl2i97m9hrx9m/Buzzer.jar

【问题讨论】:

  • 什么操作系统?
  • 他使用的是 Windows 7;我在 Mac OS X 10.6 上。该问题也出现在 Mac OS X 上的 JVM7 安装中。
  • 那么在 OSX 和 Win 7 中都存在 1.7 的错误? (我在键绑定和 1.7 和 OSX 方面遇到了一些问题,但在 Win 7 中一切正常,所以我想我会问)
  • 顺便说一句,如果您不打算使用某些方法,您可以考虑使用 KeyAdapter:docs.oracle.com/javase/7/docs/api/java/awt/event/…
  • 是的,它出现在 OSX 和 Windows 7 的 1.7 中。

标签: java freeze


【解决方案1】:

每次都创建一个新的 Clip 对象并丢弃旧的对象会更安全。试试这个方法

public static void play(String name) {
   try{
     AudioInputStream sounds = AudioSystem.getAudioInputStream(Buzzer.class.getResource(name));
     final Clip clip = AudioSystem.getClip();
     clip.addLineListener(new LineListener() {
         public void update(LineEvent e) {
             LineEvent.Type type = e.getType();
             if(type == type.STOP) clip.close();
         }
     });
     clip.open(sounds);
     clip.start();
   } catch(Exception e){
       e.printStackTrace();
   }

}

【讨论】:

  • 顺便说一句。您是否阅读过 Clip.open 的 API 文档?它说“在已经打开的行上调用此方法是非法的,可能会导致 IllegalStateException”
  • 谢谢,这成功了!不,我没有仔细查看 API,但我认为在每个 open() 之前使用 close() 会防止这种情况发生?
  • open 和 close 方法都是非阻塞的。即调用 close() 立即返回,而无需等待剪辑实际关闭。因此,当您调用 open 时,剪辑可能尚未关闭。
  • 好的。再次感谢您的帮助!
【解决方案2】:

正如建议的那样,您可以尝试使用键绑定,看看是否有帮助。这只是展示了如何使用 Enter 键。

public class Buzzer extends JFrame {
private static final String enter = "ENTER";
    public Buzzer() {
        // Key bound AbstractAction item
        enterAction = new EnterAction();
        // Gets the JFrame InputMap and pairs the key to the action
        this.getInputMap().put(KeyStroke.getKeyStroke(enter), "doEnterAction");
        // This line pairs the AbstractAction enterAction to the action "doEnterAction"
        this.getActionMap().put("doEnterAction", enterAction);
    }

    private class EnterAction extends AbstractAction {
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("Enter was pressed.");
        }
    }
}

实际上,我不记得这在应用于 JFrame 时是否有效,您可能需要先制作一个 JPanel 才能将这些应用到。只需将“this”替换为面板名称即可。

【讨论】:

  • 我是否需要为每个 KeyStroke 制定单独的规则?还是有一种简单的方法来捕获所有按键然后对它们执行逻辑?这个答案似乎不建议:stackoverflow.com/a/15422641/1642693
  • 如果您想将多个键映射到同一个函数,那么您可以在执行 getActionMap.put() 时将它们全部绑定到同一个操作,但无法执行一系列键, 键绑定旨在将单个键事件映射到操作。
猜你喜欢
  • 2018-07-18
  • 1970-01-01
  • 1970-01-01
  • 2011-03-31
  • 2016-12-31
  • 2014-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多