Java弹跳球项目:点击速度调节按钮仅修改后续新建小球速度问题咨询
问题原因分析
- 核心逻辑错误:原有代码中速度系数
change是静态成员变量,所有BallRunnable实例共享该值,修改后所有正在运行的小球线程的Thread.sleep计算都会使用新的系数,直接导致已经运行的小球速度同步变化。 - 无用的锁逻辑:你添加的
Lock完全不符合需求场景,反而会导致多个小球线程抢锁,同一时间只能有一个小球运动,和多线程并发运行的预期相悖。 - 冗余代码:
BallRunnable和BallRunnable2逻辑高度重复,无需单独定义两个类。 - Swing绘制错误:非EDT线程直接调用
paint(getGraphics())会导致绘制异常、闪烁,应该使用repaint()触发Swing的官方绘制调度。
修复方案
只需要修改BallRunnable相关逻辑即可,核心思路是:保留全局静态的默认速度系数,每个小球线程创建时,将当前的全局系数赋值给线程自身的实例成员变量,后续修改全局系数只会影响新创建的线程,已经存在的线程使用自己存储的固定系数,不会被修改。
1. 重构后的BallRunnable类(删除原有的BallRunnable2)
class BallRunnable implements Runnable { // 全局默认速度系数,仅作用于后续新建的小球 private static double globalChange = 1; public static final int STEPS = 1000; public static final int DELAY = 3; // 实例自身的速度系数,创建后固定,不受全局修改影响 private final double instanceChange; private final Ball ball; private final JPanel ballPanel; // 兼容原BallRunnable2的延迟启动逻辑 private final boolean delayStart; public BallRunnable(Ball aBall, JPanel ballPanel, boolean delayStart) { this.ball = aBall; this.ballPanel = ballPanel; this.delayStart = delayStart; // 新建实例时保存当前的全局速度系数,后续全局变更不影响该值 this.instanceChange = globalChange; } public void run() { try { if (delayStart) { Thread.sleep(100); } for (int i = 1; i <= STEPS; i++) { ball.move(ballPanel.getBounds()); // 用官方推荐的repaint触发绘制,避免闪烁和线程安全问题 ballPanel.repaint(); // 使用实例自身的固定系数计算休眠时间 Thread.sleep((long)(DELAY * instanceChange)); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } // 修改全局速度系数,仅影响后续新建的小球 public static void speedUp() { globalChange = globalChange / 2; } public static void slowDown() { globalChange = globalChange * 2; } }
2. 修改BounceFrame中的小球创建逻辑
public void addBall1() { Ball b = new Ball(); ballPanel.add(b); Runnable r = new BallRunnable(b, ballPanel, false); new Thread(r).start(); } public void addBall2() { Ball b1 = new Ball(); Ball b2 = new Ball(); ballPanel.add(b1); ballPanel.add(b2); Runnable r1 = new BallRunnable(b1, ballPanel, false); Runnable r2 = new BallRunnable(b2, ballPanel, true); new Thread(r1).start(); new Thread(r2).start(); }
3. 修改速度调整的事件逻辑
class fasterAction extends AbstractAction { public fasterAction(String name) { super(name); } public void actionPerformed(ActionEvent event) { BallRunnable.speedUp(); } } class slowerAction extends AbstractAction { public slowerAction(String name) { super(name); } public void actionPerformed(ActionEvent event) { BallRunnable.slowDown(); } }
验证效果
调整速度后,已经在运动的小球使用创建时保存的固定速度系数,速度不会发生变化,后续新建的小球会使用调整后的速度系数,符合需求。同时删除了无用的锁逻辑,解决了原有多线程阻塞的问题,替换repaint后也解决了绘制闪烁的问题。
内容的提问来源于stack exchange,提问作者iamianpark
相关产品推荐
相关产品推荐

