Java Swing渲染闪烁卡顿问题排查与解决
Java Swing方块移动闪烁卡顿问题的排查与解决
问题场景
开发Java GUI程序,实现WASD按键控制屏幕上方块移动,但运行时方块出现闪烁、卡顿,渲染效果不稳定。
核心代码
package me.analyzers.scs.game; import me.analyzers.scs.utilities.KeyPressHandler; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class MainPanel extends JPanel implements Runnable{ final int FPS = 240; final int tileSize = 16; final int scaling = 3; final int realTileSize = tileSize * scaling; final int widthX = 16; final int widthY = 12; final int screenWidth = realTileSize * widthX; final int screenHeight = realTileSize * widthY; Thread mainLoop; KeyPressHandler keyPressHandler = new KeyPressHandler(); public MainPanel() { super(); this.setPreferredSize(new Dimension(screenWidth, screenHeight)); this.setBackground(Color.WHITE); this.addKeyListener(keyPressHandler); this.setFocusable(true); } public void startGameLoop() { mainLoop = new Thread(this); mainLoop.start(); } public boolean isRunning() { return mainLoop != null; } int posX = 0; int posY = 0; @Override public void run() { long lastTime = 0; while(isRunning()) { long currentTime = System.currentTimeMillis(); if(currentTime-lastTime > 1000/FPS) { lastTime = System.currentTimeMillis(); tick(); repaint(); } } } public void tick() { switch (keyPressHandler.getLastKey()) { case 'w' -> posY-=1; case 's' -> posY+=1; case 'a' -> posX-=1; case 'd' -> posX+=1; } } @Override public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; g2d.setColor(Color.BLACK); g2d.fillRect(posX, posY, realTileSize, realTileSize); g2d.dispose(); } }
排查过程
- 初始怀疑并发问题,尝试多种游戏循环调整:
- 基于时间差条件执行逻辑
- 使用
Thread.sleep()控制帧率 - 改用Swing Timer类
以上方法均未解决卡顿闪烁问题。
- 发现若在
paintComponent内调用repaint()形成循环,渲染可正常进行,但该方法会被每秒调用数百次,不是合理方案。 - 在Windows虚拟机中运行同一Jar包,渲染流畅但方块移动速度远慢于本地机器,不符合JVM跨平台一致性预期。
- 观察到持续输入(如频繁点击窗口)可暂时消除卡顿,推测是系统为节省CPU资源限制了GUI进程的渲染频率。
解决方案
最终通过调用Toolkit.sync()解决问题,需在repaint()之后添加该方法,修改后的游戏循环代码如下:
@Override public void run() { long lastTime = 0; while(isRunning()) { long currentTime = System.currentTimeMillis(); if(currentTime-lastTime > 1000/FPS) { lastTime = System.currentTimeMillis(); tick(); repaint(); // 添加同步语句强制刷新缓冲区 Toolkit.getDefaultToolkit().sync(); } } }
原理分析
该问题源于窗口系统的图形事件缓冲机制:Swing的渲染请求可能被系统缓冲,未立即同步到屏幕,导致画面更新不及时、闪烁卡顿。Toolkit.sync()会强制将图形缓冲区的内容同步到屏幕,确保渲染操作立即生效,从而解决了卡顿和闪烁问题。
内容的提问来源于stack exchange,提问作者lzg
相关产品推荐
相关产品推荐

