如何等待一个长进程的结束(在另一个线程上)?

Sve*_*els 5 java multithreading javafx

我正在编写一个应用程序(供个人使用),它允许我通过 USB 向 Arduino 发送字符串。

我写了这个方法来发送数据:

    /**
     * Sends the data to the Arduino.
     * A new Thread is created for sending the data.
     * A transmission cool-down is started before send() method can be used again.
     * @param data the data to send to the Arduino
     */
    public void send(String data) {
        if (connected && !sending) {
            // Set 'sending' to true so only 1 Thread can be active at a time
            sending = true;
            // Create a new thread for sending the data
            Thread thread = new Thread(() -> {
                // Send the data
                PrintWriter output = new PrintWriter(chosenPort.getOutputStream());
                output.print(data);
                System.out.println("Data sended");
                output.flush();
                // Wait for the transmission cool-down and set 'sending' to false to allow for another Thread to send data
                try { Thread.sleep(transmissionCoolDown); } catch (InterruptedException interruptedException) { interruptedException.printStackTrace(); }
                sending = false;
                System.out.println("cooldown is over");
            });
            thread.start();
        }
    }
Run Code Online (Sandbox Code Playgroud)

sending是一个布尔值,用于指示线程是否正在发送数据。这transmissionCooldown只是为了在再次发送数据之前强制执行特定的等待期。

这就是使用该方法的地方:

    @FXML
    private void sendClicked() {
        // Only do something if a connection is active and an image is selected.
        // Should be unnecessary since the send button is only enables when both are true.
        if (connected && selectedIV.getImage() != null) {
            if (!sending) {
                // Save the original text of the send button and disable the disconnect button
                String ogText = sendButton.getText();
                System.out.println(ogText);
                connectButton.setDisable(true);
                // If the data has not been saved before, get the data by formatting the image
                if (data == null) {
                    data = imgCon.toStringFormat(true);
                }
                ardComm.send(data);
                // While the ArduinoCommunicator is busy sending, change the text on the send button to indicate the data is being transmitted
                sendButton.setText("busy");
                while (ardComm.isSending()) {

                }
                // Restore the text on the send button
                sendButton.setText(ogText);
                connectButton.setDisable(false);
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

sendButton是调用sendClicked()方法的 JavaFX 按钮,ardCom是包含该send()方法的类的实例。isSending()简单地返回 的发送属性,该属性ardComsend()方法开始时设置为 true,并在 Thread 完成发送时设置为 false。

问题在于这段代码:

    sendButton.setText("busy");
                while (ardComm.isSending()) {

                }
                // Restore the text on the send button
                sendButton.setText(ogText);
Run Code Online (Sandbox Code Playgroud)

我正在尝试将sendButton's text设置为 busy 以指示正在发送数据,然后循环直到数据传输完成(发送设置为 false)并以将sendButton背面的文本更改为原始文本来结束它. 我知道这可能不是实现这一目标的最佳方法,但我一直在玩,无法弄清楚为什么这不能按预期工作。

问题是由于某种原因,while 循环永远不会结束。

c0d*_*der 3

将 JavaFx 应用程序视为在单线程上运行的应用程序。
当该线程忙于运行长 while 循环 ( while (ardComm.isSending()) 时,它不会更新 gui。GUI 变得无响应(冻结)。
有关更多信息,请参阅在 JavaFX 应用程序线程上实现长时间运行的任务不可避免地会使应用程序 UI 无响应

考虑监听更改事件而不是等待 JavaFx 应用程序线程:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;

public class Main extends Application {

    private Button sendButton;

    @Override
    public void start(Stage primaryStage) throws Exception{

        ArdCom ardComm = new ArdCom();
        sendButton = new Button("Send");
        sendButton.setOnAction(event-> ardComm.send());
        ardComm.isSending().addListener((observable, oldValue, newValue) -> {
            if(newValue){
                Platform.runLater (() ->sendButton.setText("Busy"));
            }else{
                Platform.runLater (() ->sendButton.setText("Send"));
            }
        });
        AnchorPane root = new AnchorPane(sendButton);
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

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

class ArdCom {

    private final ReadOnlyBooleanWrapper sending = new ReadOnlyBooleanWrapper(false);

    void send() {//synchronize if accessed from multi thread

        if (! sending.get() ) {
            sending.set(true);
            Thread thread = new Thread(() -> {
                System.out.println("Send started");
                try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); }
                sending.set(false);
                System.out.println("Send completed");
            });
            thread.start();
        }
    }

    SimpleBooleanProperty isSending(){
        return sending;
    }
}
Run Code Online (Sandbox Code Playgroud)