如何使用暂停,线程(任务,服务)模拟队列和堆栈JavaFX

时间:2018-03-20 16:55:17

标签: java multithreading javafx

我一直在搜索YouTube,Stack-Overflow和fxexperience,甚至oracle documentation,但我仍然没有得到它。 这里没有类似的例子:(

问题是如何进行堆栈和队列模拟器。

  1. 生成10个随机数。完成。
  2. 在表格中显示数字。完成。
  3. 使用10个随机数来模拟堆栈和队列。我现在不知道如何使用TextField来协作服务。
  4. 暂停模拟。或者停止。
  5. - 程序需要暂停方法。我不知道如何暂停一个thead。也许用wait()和notify()。我不知道。

    我使用过label.textProperty.bind(service.progressProperty())。这工作,但当我尝试绑定变量时 方法updateProgress(i,n)抛出异常。

    也许我需要使用2个任务。

    主要课程:

    package simulation;
    
    import javafx.application.Application;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Parent;
    import javafx.scene.Scene;
    import javafx.stage.Stage;
    
    public class Main extends Application {
    
    @Override
    public void start(Stage primaryStage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getResource("/simulation/simulation.fxml"));
        primaryStage.setTitle("JavaFX and concurrency, Stack and Queue");
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }
    
    
    public static void main(String[] args) {
        launch(args);
    }
    }
    

    控制器类:

    package simulation;
    
    import javafx.application.Platform;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.concurrent.Worker;
    import javafx.fxml.FXML;
    import javafx.fxml.Initializable;
    import javafx.scene.control.*;
    import javafx.scene.control.cell.PropertyValueFactory;
    
    import java.net.URL;
    import java.util.ResourceBundle;
    
    public class SimulationCt implements Initializable {
    
      @FXML private TableView table;
      @FXML private TableColumn i_tc;
      @FXML private TableColumn random_tc;
    
      @FXML private TextField stack_start;
      @FXML private TextField stack_1;
      @FXML private TextField stack_2;
      @FXML private TextField stack_3;
      @FXML private TextField stack_4;
      @FXML private TextField stack_5;
      @FXML private TextField stack_final;
    
      @FXML private TextField queue_start;
      @FXML private TextField queue_1;
      @FXML private TextField queue_2;
      @FXML private TextField queue_3;
      @FXML private TextField queue_4;
      @FXML private TextField queue_5;
      @FXML private TextField queue_final;
    
      @FXML private Button new_b;
      @FXML private Button play_pause_b;
      @FXML private Button stop_b;
    
      @FXML private ProgressBar progress_bar;
    
      private ObservableList<RandomNumber> numberList = FXCollections.observableArrayList();
    
      private CalculateService backProcess;
    
      @FXML
      private void createNew () {
        disableNew(true);
        generateRandom();
        backProcess = new CalculateService();
        progress_bar.progressProperty().bind(backProcess.progressProperty());
        Platform.runLater(() -> {
          backProcess.start();
        });
      }
    
      @FXML
      private void playPause () {
        if(backProcess.getState().equals(Worker.State.RUNNING)) {
          System.out.println("stoping...");
          backProcess.cancel();
        } else if (backProcess.getState().equals(Worker.State.CANCELLED)) {
          System.out.println("restarting...");
          backProcess.restart();
        }
      }
    
      @FXML
      private void stop () {
        if(backProcess.getState().equals(Worker.State.RUNNING)) {
          System.out.println("stoping...");
          backProcess.cancel();
        } else if (backProcess.getState().equals(Worker.State.CANCELLED)) {
          System.out.println("already stoped...");
        }
        clearItems();
        disableNew(false);
      }
    
      // cleans the list and the progress bar.
      private void clearItems () {
        progress_bar.progressProperty().unbind();
        progress_bar.progressProperty().set(0.0);
        numberList.clear();
      }
    
      private void disableNew (boolean b) {
        new_b.setDisable(b);
        play_pause_b.setDisable(!b);
        stop_b.setDisable(!b);
      }
    
      // generates random numbers to fill the table, these numbers are the ones for the stack and the queue.
      private void generateRandom () {
        for (int i = 1; i < 11; i++) {
          int rnd = (int)(Math.random() * (200 - 0 + 1)) + 0;
          numberList.add(new RandomNumber(i, rnd ));
        }
      }
    
      private void startTable () {
        i_tc.setCellValueFactory( new PropertyValueFactory("i"));
        random_tc.setCellValueFactory( new PropertyValueFactory("number"));
        table.setItems(numberList);
      }
    
      @Override
      public void initialize(URL url, ResourceBundle rb) {
        disableNew(false);
        startTable();
      }
    }
    

    FXML:

      <?xml version="1.0" encoding="UTF-8"?>
    
      <?import javafx.scene.control.Button?>
      <?import javafx.scene.control.Label?>
      <?import javafx.scene.control.ProgressBar?>
      <?import javafx.scene.control.TableColumn?>
      <?import javafx.scene.control.TableView?>
      <?import javafx.scene.control.TextField?>
      <?import javafx.scene.layout.AnchorPane?>
      <?import javafx.scene.layout.ColumnConstraints?>
      <?import javafx.scene.layout.GridPane?>
      <?import javafx.scene.layout.HBox?>
      <?import javafx.scene.layout.RowConstraints?>
      <?import javafx.scene.layout.VBox?>
    
      <AnchorPane id="AnchorPane" prefHeight="300.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="simulation.SimulationCt">
      <children>
            <TableView fx:id="table" layoutX="8.0" layoutY="10.0" prefHeight="282.0" prefWidth="162.0">
              <columns>
                <TableColumn fx:id="i_tc" prefWidth="28.0" text="i" />
                <TableColumn fx:id="random_tc" prefWidth="122.0" text="Random Number" />
              </columns>
            </TableView>
            <GridPane layoutX="303.0">
               <columnConstraints>
                  <ColumnConstraints fillWidth="false" minWidth="10.0" prefWidth="50.0" />
                  <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
                  <ColumnConstraints fillWidth="false" halignment="RIGHT" hgrow="SOMETIMES" minWidth="10.0" prefWidth="50.0" />
               </columnConstraints>
               <rowConstraints>
                  <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                  <RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
               </rowConstraints>
               <children>
                  <Label text="Stack" />
                  <VBox GridPane.rowIndex="1" GridPane.valignment="TOP">
                     <children>
                        <TextField fx:id="stack_start" prefHeight="25.0" prefWidth="25.0" />
                        <Label text="new" />
                     </children>
                  </VBox>
                  <VBox GridPane.columnIndex="1" GridPane.rowIndex="1">
                     <children>
                        <TextField fx:id="stack_1" prefHeight="25.0" prefWidth="25.0" />
                        <TextField fx:id="stack_2" prefHeight="25.0" prefWidth="25.0" />
                        <TextField fx:id="stack_3" prefHeight="25.0" prefWidth="25.0" />
                        <TextField fx:id="stack_4" prefHeight="25.0" prefWidth="25.0" />
                        <TextField fx:id="stack_5" prefHeight="25.0" prefWidth="25.0" />
                     </children>
                  </VBox>
                  <VBox GridPane.columnIndex="2" GridPane.rowIndex="1" GridPane.valignment="TOP">
                     <children>
                        <TextField fx:id="stack_final" prefHeight="25.0" prefWidth="25.0" />
                        <Label text="last" />
                     </children>
                  </VBox>
               </children>
            </GridPane>
            <GridPane layoutX="193.0" layoutY="155.0">
               <columnConstraints>
                  <ColumnConstraints fillWidth="false" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
                  <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
                  <ColumnConstraints fillWidth="false" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
               </columnConstraints>
               <rowConstraints>
                  <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                  <RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
               </rowConstraints>
               <children>
                  <Label text="Queue" GridPane.columnIndex="1" />
                  <VBox GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="TOP">
                     <children>
                        <TextField fx:id="queue_start" prefHeight="25.0" prefWidth="25.0" />
                        <Label text="new" />
                     </children>
                  </VBox>
                  <HBox spacing="5.0" GridPane.columnIndex="1" GridPane.rowIndex="1">
                     <children>
                        <TextField fx:id="queue_1" prefHeight="25.0" prefWidth="25.0" />
                        <TextField fx:id="queue_2" prefHeight="25.0" prefWidth="25.0" />
                        <TextField fx:id="queue_3" prefHeight="25.0" prefWidth="25.0" />
                        <TextField fx:id="queue_4" prefHeight="25.0" prefWidth="25.0" />
                        <TextField fx:id="queue_5" prefHeight="25.0" prefWidth="25.0" />
                     </children>
                  </HBox>
                  <VBox GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="TOP">
                     <children>
                        <TextField fx:id="queue_final" prefHeight="25.0" prefWidth="25.0" />
                        <Label text="last" />
                     </children>
                  </VBox>
               </children>
            </GridPane>
            <Button fx:id="new_b" onAction="#createNew" layoutX="266.0" layoutY="243.0" mnemonicParsing="false" text="New" />
            <Button fx:id="play_pause_b" onAction="#playPause" layoutX="326.0" layoutY="243.0" mnemonicParsing="false" text="Play / Pause" />
            <Button fx:id="stop_b" onAction="#stop" layoutX="428.0" layoutY="243.0" mnemonicParsing="false" text="Stop" />
            <ProgressBar fx:id="progress_bar" layoutX="266.0" layoutY="277.0" prefWidth="200.0" progress="0.0" />
      </children>
      </AnchorPane>
    

    DataHelper:

    package simulation;
    
    import javafx.beans.property.IntegerProperty;
    import javafx.beans.property.SimpleIntegerProperty;
    
    public class RandomNumber {
    
      private IntegerProperty i;
      private IntegerProperty number;
    
      public RandomNumber(int i, int number) {
        this.i = new SimpleIntegerProperty(i);
        this.number = new SimpleIntegerProperty(number);
      }
    
      public int getI() {
        return i.get();
      }
    
      public IntegerProperty iProperty() {
        return i;
      }
    
      public void setI(int i) {
        this.i.set(i);
      }
    
      public int getNumber() {
        return number.get();
      }
    
      public IntegerProperty numberProperty() {
        return number;
      }
    
      public void setNumber(int number) {
        this.number.set(number);
      }
    
    }
    

    服务类:

    package simulation;
    
    import javafx.concurrent.Service;
    import javafx.concurrent.Task;
    
    public class CalculateService extends Service {
      int n = 20; // this does the trick to simulate the pause.
      int j = 0; // even if the task is canceled the last value is saved here.
    
      @Override
      protected Task createTask() {
        return new Task() {
          @Override protected Void call() throws Exception {
            int a;
            int b;
            int iterations;
            for (iterations = j; iterations <= n; iterations++) {
              j = iterations;
              if (isCancelled()) {
                updateMessage("Cancelled");
                break;
              }
              updateProgress(iterations, n);
              System.out.println("number: " + j);
    
              //Block the thread for a short time, but be sure
              //to check the InterruptedException for cancellation
              try {
                Thread.sleep(100);
              } catch (InterruptedException interrupted) {
                if (isCancelled()) {
                  updateMessage("Cancelled");
                  break;
                }
              }
            }
            return null;
          }
        };
      }
    }
    

    enter image description here

1 个答案:

答案 0 :(得分:1)

问题中有很多代码,我认为你不需要全部来解决你实际要问的概念。所以我在这里给出一个高级答案。如果您想将问题编辑为更简单的问题以解决实际问题,那么我可以将此问题特定于该示例。

我可能会在没有线程的情况下尝试这样做,但是使用动画API。例如,您可以使用Timeline,其基本大纲如下:

public class Controller {

    // @FXML-annotated UI elements...
    // Other state....

    private Timeline timeline ;

    @FXML
    public void initialize() {

        timeline = new Timeline(new KeyFrame(Duration.seconds(100)), e -> {
            if (moreStepsToDo()) {
                doNextStep();
            } else {
                stopSimulation();
            }
        });
        timeline.setCycleCount(Animation.INDEFINITE);
    }

    private boolean moreStepsToDo() {
        // return true if there are more steps in the simulation,
        // false otherwise
    }

    private void doNextStep() {
        // do next step in the simulation
    }

    @FXML
    private void stopSimulation() {
        timeline.stop();
    }

    @FXML
    private void pauseSimulation() {
        timeline.pause();
    }

    @FXML
    private void playSimulation() {
        timeline.play();
    }

    @FXML
    private void resetSimulation() {
        timeline.jumpTo(Duration.ZERO);
    }
}

这个解决方案的优点在于一切都是单线程的:关键帧的事件处理程序在FX Application Thread上执行,它与执行事件处理程序的线程相同。这意味着无需担心跨线程同步数据。动画API pause()play()stop()中的预定义方法提供了您正在寻找的功能;你只需要适当地更新应用程序状态。

这是一个使用这种方法的简单完整示例(它只是将一堆矩形,一次一个,从一个vbox移动到另一个)。

import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;

public class SimplePausableAnimation extends Application {

    private VBox left;
    private VBox right;
    private Timeline timeline;
    private Button pausePlay;

    @Override
    public void start(Stage primaryStage) {
        left = new VBox(10);
        left.setMinWidth(200);
        right = new VBox(10);
        right.setMinWidth(200);

        HBox hbox = new HBox(10, left, right);

        pausePlay = new Button();

        Button reset = new Button("Reset");
        reset.setOnAction(e -> reset());

        reset();

        BorderPane root = new BorderPane(hbox);

        HBox buttons = new HBox(5, pausePlay, reset);
        buttons.setAlignment(Pos.CENTER);

        root.setBottom(buttons);

        Scene scene = new Scene(root, 600, 600);

        primaryStage.setScene(scene);
        primaryStage.show();

    }

    private void reset() {

        if (timeline != null) {
            timeline.stop();
        }

        left.getChildren().clear();
        right.getChildren().clear();

        for (int i = 0; i < 5; i++) {
            left.getChildren().add(new Rectangle(100, 100, Color.CORNFLOWERBLUE));
        }

        timeline = new Timeline(new KeyFrame(Duration.seconds(1), e -> {
            if (moreStepsToDo()) {
                doNextStep();
            } else {
                timeline.stop();
            }
        }));
        timeline.setCycleCount(Animation.INDEFINITE);

        pausePlay.disableProperty().bind(Bindings.createBooleanBinding(() -> {
            if (left.getChildren().isEmpty()) {
                return true;
            }
            return false;
        }, left.getChildren()));

        pausePlay.textProperty().bind(Bindings.createStringBinding(() -> {
            if (timeline.getStatus() == Animation.Status.RUNNING) {
                return "Pause";
            }
            return "Play";
        }, timeline.statusProperty()));

        pausePlay.setOnAction(e -> {
            if (timeline.getStatus() == Animation.Status.RUNNING) {
                timeline.pause();
            } else {
                timeline.play();
            }
        });
    }

    private boolean moreStepsToDo() {
        return !left.getChildren().isEmpty();
    }

    private void doNextStep() {
        int n = left.getChildren().size();
        Node node = left.getChildren().remove(n - 1);
        right.getChildren().add(node);
    }

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

如果您确实希望使用线程执行此操作,则暂停线程的一种方法是使用带有单个许可的Semaphore。这通常看起来像这样:

Semaphore pauser = new Semaphore(1);

Thread simulationThread = new Thread(() -> {
    try {
        while (! Thread.currentThread().isInterrupted()) {
            pauser.acquire();
            // do simulation step
            pauser.release();
            Thread.sleep(100);
        }
    } catch (InterruptedException exc) {
        // ignore and exit thread...
    }
});

(显然,同一个成语将在你的Task中起作用,后者在后台线程上执行。)

然后从控制器调用pauser.acquire();将暂停模拟(因为模拟线程将无法获取许可),并且在暂停时调用pauser.release()会让它再次运行。 / p>