如何在Swing/AWT事件队列中添加指定时长暂停?最佳实践
Swing/AWT事件间添加指定延迟的最佳实践
我开发了一款游戏,需要让某个组件闪烁红色后,经过短暂延迟恢复至正常颜色。用Thread.sleep(123)会直接阻塞事件调度线程(EDT),导致整个UI完全失去响应,显然不是可行方案。虽然可以在独立线程中执行暂停后调用SwingUtilities::invokeLater更新UI,但这类场景下有更标准的最佳实践。
问题示例代码
用户提供的示例代码中,在EDT内调用Thread.sleep(10_000)会导致窗口10秒内无法交互:
import javax.swing.*; import java.awt.*; public class cry { public static void main(final String[] args) { SwingUtilities.invokeLater(cry::blah); } private static void blah() { final JFrame frame = new JFrame(); frame.setTitle("ABC"); frame.setLocationByPlatform(true); frame.setSize(300, 300); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); try { Thread.sleep(10_000); //sleep for 10 seconds } catch (Exception e) { throw new RuntimeException(e); } frame.setTitle("XYZ"); } }
最优解决方案:使用javax.swing.Timer
Swing提供的Timer是专门为EDT环境设计的延迟任务工具,所有回调逻辑都会自动在EDT上执行,既不会阻塞UI,又能保证UI操作的线程安全性,是这类场景的标准选择。
改造示例代码(替换sleep实现标题延迟更新)
将原代码中的Thread.sleep替换为Timer,实现10秒后更新标题且不阻塞UI:
import javax.swing.*; import java.awt.*; public class cry { public static void main(final String[] args) { SwingUtilities.invokeLater(cry::blah); } private static void blah() { final JFrame frame = new JFrame(); frame.setTitle("ABC"); frame.setLocationByPlatform(true); frame.setSize(300, 300); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); // 创建一次性Timer,延迟10秒触发 Timer titleUpdateTimer = new Timer(10000, e -> { frame.setTitle("XYZ"); // 执行完毕后停止Timer(因为设置了只执行一次,这步也可以省略) ((Timer) e.getSource()).stop(); }); titleUpdateTimer.setRepeats(false); // 设置为仅执行一次 titleUpdateTimer.start(); // 启动Timer } }
组件闪烁场景的实现
针对游戏中组件闪烁红色后恢复的需求,用Timer可以轻松实现:
import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; public class ComponentBlinkExample { public static void main(String[] args) { SwingUtilities.invokeLater(ComponentBlinkExample::createUI); } private static void createUI() { JFrame frame = new JFrame("组件闪烁示例"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(300, 200); frame.setLayout(new FlowLayout()); JButton blinkButton = new JButton("点击闪烁"); frame.add(blinkButton); // 保存按钮原始背景色 Color originalColor = blinkButton.getBackground(); blinkButton.addActionListener((ActionEvent e) -> { // 第一步:将按钮设为红色 blinkButton.setBackground(Color.RED); // 创建延迟恢复颜色的Timer,500毫秒后触发 Timer blinkTimer = new Timer(500, event -> { blinkButton.setBackground(originalColor); ((Timer) event.getSource()).stop(); }); blinkTimer.setRepeats(false); blinkTimer.start(); }); frame.setVisible(true); } }
为什么Timer是最优选择?
- 线程安全:Timer的回调自动在EDT执行,无需手动调用
invokeLater,避免了线程安全问题。 - 不阻塞UI:Timer不会占用EDT的执行时间,UI在延迟期间仍能正常响应交互。
- 简洁可控:支持重复执行、随时启停,比手动管理线程+延迟的实现更简洁可靠。
内容的提问来源于stack exchange,提问作者davidalayachew
相关产品推荐
相关产品推荐

