Java/JavaFX:轮询数据库以进行单值更新,同时保持 GUI 中的响应能力

我和一些朋友一起,试图创建一个回合制游戏。我们在检查用户何时轮到他们时遇到了一些问题,同时保持GUI的响应性,以及在游戏关闭时关闭我们现在使用的线程。我希望获得有关如何执行此操作的一些信息,但我不确定问题是与JavaFX相关,还是与线程相关,还是两者兼而有之。


我试图尽可能多地搜索,但根本找不到我想要的东西,即使我相信它很简单。现在,我们有一个线程运行一个循环,当你点击一个按钮来检查它是否轮到你了。当轮到你时,我们希望禁用一些用户输入,所以我不确定我们是否真的需要一个线程,除了保持响应能力。


我也尝试过实现一个扩展 Thread 的类,但这似乎只会让问题变得更糟,因为每次玩家没有轮到它时都会启动一个新线程,或者如果我将循环放在线程之外,则会冻结 GUI。


public void refreshButtonPressed(){

    try{

        refreshButton.setDisable(true);

        Thread pollThread = new Thread(() -> {

            System.out.println("Thread started");  //Stop being able to start more threads

            int user_id = 0;

            String gamePin = "xxxxxx";

        while (!GameConnection.yourTurn(user_id, Context.getContext().getGamePin())){ //This method checks the database if it is your turn

            try{

                Thread.sleep(5000);  //So we don't flood the database

            }

            catch (InterruptedException e){

                System.out.println("Interrupted");

                break;

            }

             //If we close the game, stop the thread/while loop. 

            if (TurnPolling.closedGame){

                break;

            }

        }


        playerButton.setDisable(false);

        refreshButton.setDisable(false);

        refreshButton.setText("Refresh");

        System.out.println("Thread ended");

        });

        pollThread.start();

    }catch (Exception e){

        e.printStackTrace();

    }

}

在游戏屏幕.fxml文件的控制器中(不是主屏幕,而是通过登录屏幕和主扩展应用程序加载的屏幕)。


public void initialize(URL location, ResourceBundle resources) {

    playerButton.setDisable(!GameConnection.yourTurn(user_id, gameTurn));

    myStage.setOnCloseRequest(event -> TurnPolling.closedGame = true);

}

目前,TurnPolling 类只有公共静态布尔封闭游戏,以免将其保留在控制器中。最后一行设置封闭游戏 = true 实际上给了我一个空点器例外,这可能是因为舞台尚未初始化,当我在 initialize() 方法中执行此操作时?


我希望仅在轮到玩家时启用玩家按钮,并在游戏屏幕关闭时关闭线程(如果需要)。现在,你必须点击一个按钮来检查它是否轮到你了,每五秒钟再次检查一次,当你关闭游戏时,它不会停止。


请告诉我,如果你需要更多的代码或澄清,这是我的第一个大项目,所以我真的不知道放多少在这里。我知道这不是有效的代码,但它是我能做的,而不会让它感觉像是混乱。感谢您的回答!


烙印99
浏览 268回答 1
1回答

犯罪嫌疑人X

首先,重要的是要记住,不允许在 JavaFX 应用程序线程以外的任何线程中更改 JavaFX 节点。因此,您的线程需要移动以下行:playerButton.setDisable(false);refreshButton.setDisable(false);refreshButton.setText("Refresh");到一个可运行的,它被传递给平台。Platform.runLater(() -> {&nbsp; &nbsp; playerButton.setDisable(false);&nbsp; &nbsp; refreshButton.setDisable(false);&nbsp; &nbsp; refreshButton.setText("Refresh");});请注意,对一个线程中的字段所做的更改在另一个线程中可能不可见,除非声明为 。从 Java 语言规范:TurnPolling.closedGamevolatile例如,在下面的(损坏的)代码片段中,假定这是一个非字段:this.donevolatilebooleanwhile (!this.done)&nbsp; &nbsp; Thread.sleep(1000);编译器可以自由地读取该字段一次,并在每次执行循环时重用缓存的值。这意味着循环永远不会终止,即使另一个线程更改了 的值。this.donethis.done使用任务和服务JavaFX为所有这些提供了一个更干净的解决方案:任务和服务。服务创建任务。服务具有可绑定的值属性,该属性始终等于最近创建的任务的值。您可以将按钮属性绑定到服务的值属性:int user_id = 0;Service<Boolean> turnPollService = new Service<Boolean>() {&nbsp; &nbsp; @Override&nbsp; &nbsp; protected Task<Boolean> createTask() {&nbsp; &nbsp; &nbsp; &nbsp; return new Task<Boolean>() {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; @Override&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; protected Boolean call()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; throws InterruptedException {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; updateValue(true);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; String gamePin = Context.getContext().getGamePin();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; while (!GameConnection.yourTurn(user_id, gamePin)) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Thread.sleep(5000);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (TurnPolling.closedGame){&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return false;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; };&nbsp; &nbsp; }};playerButton.disableProperty().bind(turnPollService.valueProperty());refreshButton.disableProperty().bind(turnPollService.valueProperty());refreshButton.textProperty().bind(&nbsp; &nbsp; Bindings.when(&nbsp; &nbsp; &nbsp; &nbsp; turnPollService.valueProperty().isEqualTo(true))&nbsp; &nbsp; &nbsp; &nbsp; .then("Waiting for your turn\u2026")&nbsp; &nbsp; &nbsp; &nbsp; .otherwise("Refresh"));当玩家的回合完成时,您将调用 。turnPollService.restart();无论您是使用服务,还是仅使用 Platform.runLater,您仍然需要通过创建它或将所有对它的访问包含在块(或锁保护)中来使线程安全。TurnPolling.closedGamevolatilesynchronized
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java