You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaFX多线程应用问题:定时控制挖掘机对象移动

解决JavaFX多线程挖掘机定时动作问题

我来帮你梳理并解决这个定时控制挖掘机动作的问题。首先明确你的核心需求:

  • 5台挖掘机在00:00-00:04:59和01:05-24:00时段停留在窗口中心
  • 00:05:00-00:34:59执行向上移动并持续半小时
  • 00:35:00-01:04:59执行向下移动并持续半小时

先看你现有代码里的几个关键问题:

  • Time线程每次调用setTime都会新建Excavators线程,这会导致线程泛滥,完全没必要
  • Excavators类的run方法不完整,没有实现任何动作逻辑
  • 每个挖掘机单独写move方法,代码冗余严重
  • 时间推进的sleep是10ms,相当于时间流速是真实的100倍(如果是模拟加速没问题,我默认按真实时间每秒推进,你可以按需调整)

下面是修正后的完整解决方案:


第一步:重构Excavators类,统一管理所有挖掘机

我们把5个ImageView封装到Excavators类里,统一处理它们的位置逻辑,避免重复代码:

import javafx.application.Platform;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.StackPane;

public class Excavators {
    private final ImageView[] excavators;
    private final StackPane root;
    // 中心位置坐标(StackPane中TranslateX/Y为0就是中心)
    private static final double CENTER_X = 0;
    private static final double CENTER_Y = 0;
    // 移动步长(可根据需求调整移动速度)
    private static final double MOVE_STEP = 1;
    // 移动方向标记:1=向上,-1=向下,0=停留
    private int moveDirection = 0;

    public Excavators(StackPane root, Image excavatorImage) {
        this.root = root;
        this.excavators = new ImageView[5];
        // 初始化5台挖掘机
        for (int i = 0; i < excavators.length; i++) {
            excavators[i] = new ImageView(excavatorImage);
            // 初始位置设为中心
            resetToCenter();
            root.getChildren().add(excavators[i]);
        }
        // 启动单独的移动线程,控制移动帧率
        startMovementThread();
    }

    // 让所有挖掘机回到中心位置并停留
    public void stay() {
        moveDirection = 0;
        Platform.runLater(this::resetToCenter);
    }

    // 触发向上移动
    public void moveUp() {
        moveDirection = 1;
    }

    // 触发向下移动
    public void moveDown() {
        moveDirection = -1;
    }

    // 重置所有挖掘机到中心位置
    private void resetToCenter() {
        for (ImageView excavator : excavators) {
            excavator.setTranslateX(CENTER_X);
            excavator.setTranslateY(CENTER_Y);
        }
    }

    // 处理持续移动逻辑的线程
    private void startMovementThread() {
        new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                if (moveDirection != 0) {
                    Platform.runLater(() -> {
                        for (ImageView excavator : excavators) {
                            // 向上移动时Y坐标减小,向下时增大
                            excavator.setTranslateY(excavator.getTranslateY() - moveDirection * MOVE_STEP);
                        }
                    });
                }
                try {
                    Thread.sleep(40); // 控制移动帧率,和你原代码保持一致
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }).start();
    }
}

第二步:修正Time线程,准确控制时间并触发动作

现在Time线程只负责推进时间,并根据当前时间调用Excavators对应的方法,不再重复创建线程:

public class Time extends Thread {
    private int hour = 0;
    private int minute = 0;
    private int second = 0;
    private final Main main;
    private final Excavators excavators;

    public Time(Main main, Excavators excavators) {
        this.main = main;
        this.excavators = excavators;
    }

    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                // 更新时间逻辑
                second++;
                if (second == 60) {
                    second = 0;
                    minute++;
                }
                if (minute == 60) {
                    minute = 0;
                    hour++;
                }
                if (hour == 24) {
                    hour = 0;
                    minute = 0;
                    second = 0;
                }

                // 根据当前时间判断并触发挖掘机动作
                checkAndTriggerExcavatorAction();

                // 更新UI上的时间显示
                main.updateTime(hour, minute, second);
                System.out.printf("%02d:%02d:%02d%n", hour, minute, second);

                // 真实时间每秒推进一次,想加速就改小这个值(比如10ms=100倍速)
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    // 判断当前时间对应的挖掘机动作
    private void checkAndTriggerExcavatorAction() {
        boolean isStayPeriod1 = (hour == 0 && minute < 5);
        boolean isMoveUpPeriod = (hour == 0 && minute >=5 && minute <35);
        boolean isMoveDownPeriod = (hour ==0 && minute >=35) || (hour ==1 && minute <5);
        boolean isStayPeriod2 = (hour >1) || (hour ==1 && minute >=5);

        if (isStayPeriod1 || isStayPeriod2) {
            excavators.stay();
        } else if (isMoveUpPeriod) {
            excavators.moveUp();
        } else if (isMoveDownPeriod) {
            excavators.moveDown();
        }
    }
}

第三步:简化Main类,整合核心逻辑

现在Main类不需要单独管理每个ImageView,只需要初始化Excavators和Time线程即可:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;

public class Main extends Application {
    private StackPane root = new StackPane();
    private Text timeDisplay = new Text();
    private Excavators excavators;

    @Override
    public void start(Stage primaryStage) throws Exception {
        root.setStyle("-fx-background-color: #00FF00");
        primaryStage.setTitle("Excavator Scheduler");
        primaryStage.setScene(new Scene(root, 1000, 800));

        // 初始化挖掘机
        Image excavatorImage = new Image("resources/excavator.png");
        excavators = new Excavators(root, excavatorImage);

        // 初始化时间显示控件
        timeDisplay.setTranslateX(-450);
        timeDisplay.setTranslateY(-380);
        root.getChildren().add(timeDisplay);

        // 启动时间线程
        Time timeThread = new Time(this, excavators);
        timeThread.start();

        // 关闭窗口时终止线程,避免资源泄漏
        primaryStage.setOnCloseRequest((WindowEvent t) -> {
            timeThread.interrupt();
            Platform.exit();
            System.exit(0);
        });

        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

    // 更新UI上的时间显示(确保在JavaFX主线程执行)
    public void updateTime(int hour, int minute, int second) {
        String timeStr = String.format("%02d:%02d:%02d", hour, minute, second);
        Platform.runLater(() -> timeDisplay.setText(timeStr));
    }
}

关键改进点说明

  1. 代码复用:把5台挖掘机的管理统一到Excavators类,消除了重复的moveExcavator1~5方法
  2. 线程安全:所有UI更新都通过Platform.runLater确保在JavaFX主线程执行,避免线程安全问题
  3. 逻辑清晰:时间判断和动作触发分离,Time只负责时间推进,Excavators负责动作执行
  4. 避免线程泛滥:只创建一个Time线程和一个移动线程,不再重复创建Excavators线程
  5. 边界处理:准确覆盖所有时间区间,包括01:00-01:04属于向下移动时段,01:05之后回到停留状态

内容的提问来源于stack exchange,提问作者Marcin Frąckiewicz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:45:20