编辑:一个用户标记我的问题可能是这个问题的重复:“ What is the volatile keyword useful for ”,其标题是“What is the volatile keyword useful for?”。我读了这个问题,但我不明白它与我的问题有什么关系。这是一个用两个 .java 文件编写的程序。我的问题涉及主要方法中的 if..else..。请注意在下面的代码中,else {..} 中的单行被注释掉了。我将这个程序称为“版本 1”,我将在“版本 2”中将该行注释掉的程序称为该程序。// -------------// The code below is in IfElseBugProgram.javapublic class IfElseBugProgram { public static void main(String[] args) { MyJFrame terminal = new MyJFrame(); while (true) { String keyReleased = terminal.getKeyReleased(); if (! keyReleased.equals("") ) { System.out.print("@" + keyReleased); } else { // System.out.print("!" + keyReleased); } } }}// ----- //The code below is in file MyJFrame.javaimport javax.swing.JFrame;import java.awt.event.KeyEvent;import java.awt.event.KeyListener;import java.util.ArrayList;import java.util.List;public class MyJFrame extends JFrame implements KeyListener{ private List<KeyEvent> keyEventQueue; public MyJFrame() { keyEventQueue = new ArrayList<KeyEvent>(); this.addKeyListener(this); pack(); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setVisible(true); } public void keyPressed(KeyEvent e) { } public void keyTyped(KeyEvent e) { } public void keyReleased(KeyEvent keyEvent) { keyEventQueue.add(keyEvent); System.out.println("some key was released!" + keyEventQueue.size()); }我希望 if {...} 中的代码在我按下键盘上的某个键后运行。也就是说,我希望 System.out.print("@" + keyReleased); 代码在我按下一个键时立即运行。使用版本 1,我似乎永远不会System.out.print("@" + keyReleased);被运行;控制台中永远不会打印“@1”或“@2”或“@3”等。对于版本 2(即 else {..} 块中的代码重新注释),通常发生的是打印出“!”的打印语句。重复运行,直到我按下一个键。那时,诸如“@1”或“@2”之类的东西会被重复打印。有时发生的是我没有得到“!” 也没有打印出“@1”或“@2”!(使用相同的源代码!)问题:为什么System.out.print("@" + keyReleased);if {..} 块中的行在版本 1 中不运行,但(通常)在版本 2 中运行?
2 回答
富国沪深
TA贡献1790条经验 获得超9个赞
通过假定变量不会被并发线程修改,Java VM 可以优化连续的、非同步的加载。
如果你想在一个将被另一个线程更改的字段上进行自旋循环,你可以让虚拟机通过将其标记为来确保每次读取都能看到最新的更改volatile
:
private volatile List<KeyEvent> keyEventQueue;
对 volatile 字段 (§8.3.1.4) 的写入发生在对该字段的每次后续读取之前。
我不知道你的 V2 是否保证按照 JLS 工作,但System.out
PrintStream
每次写入时都会同步,从而限制允许 VM 进行的优化。
潇潇雨雨
TA贡献1833条经验 获得超4个赞
您遇到这种不可预测的行为的主要原因是因为您实际上创建了一个多线程程序,您在其中从event dispatching thread以外的线程访问 Swing 组件。
具体行:
MyJFrame terminal = new MyJFrame();
启动 Swing 事件调度线程但行(例如):
String keyReleased = terminal.getKeyReleased();
terminal
从主线程访问(Swing 组件)。
来自Swing 包文档:
一般来说,Swing 不是线程安全的。除非另有说明,否则所有 Swing 组件和相关类都必须在事件调度线程上访问。
在继续尝试使此代码工作之前,我建议您阅读教程课程:Swing 中的并发。
添加回答
举报
0/150
提交
取消