如何使用暂停,线程(任务,服务)模拟队列和堆栈JavaFX [英] How to simulate a queue and a stack JavaFX with pause, threads(task, service)

查看:50
本文介绍了如何使用暂停,线程(任务,服务)模拟队列和堆栈JavaFX的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在YouTube,Stack-Overflow和fxexperience中搜索,甚至在oracle

解决方案

问题中有很多代码,我认为您并不需要全部代码来解决您实际询问的概念.因此,我在这里只给出一个高层次的答案.如果您想将问题编辑为更简单的解决实际问题的方法,那么我可以针对该示例进行具体说明.

我可能会尝试完全不使用线程来执行此操作,而是使用动画API.例如,您可以使用具有以下基本轮廓的 Timeline :

 公共类控制器{//@FXML注释的UI元素...//其他状态...私有时间线时间线;@FXML公共无效initialize(){时间轴=新时间轴(新KeyFrame(Duration.seconds(100)),e-> {如果(moreStepsToDo()){doNextStep();} 别的 {stopSimulation();}});timeline.setCycleCount(Animation.INDEFINITE);}私人布尔moreStepsToDo(){//如果模拟中还有更多步骤,则返回true,//否则为false}私人void doNextStep(){//做模拟的下一步}@FXML私人无效stopSimulation(){timeline.stop();}@FXML私人无效的pauseSimulation(){timeline.pause();}@FXML私人无效playSimulation(){timeline.play();}@FXML私人无效resetSimulation(){timeline.jumpTo(Duration.ZERO);}} 

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

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

  import javafx.animation.Animation;导入javafx.animation.KeyFrame;导入javafx.animation.Timeline;导入javafx.application.Application;导入javafx.beans.binding.Bindings;导入javafx.geometry.Pos;导入javafx.scene.Node;导入javafx.scene.Scene;导入javafx.scene.control.Button;导入javafx.scene.layout.BorderPane;导入javafx.scene.layout.HBox;导入javafx.scene.layout.VBox;导入javafx.scene.paint.Color;导入javafx.scene.shape.Rectangle;导入javafx.stage.Stage;导入javafx.util.Duration;公共类SimplePausableAnimation扩展了Application {私人VBox左;私人VBox权利;专用时间轴时间表;私人Button pausePlay;@Overridepublic void start(Stage primaryStage){左=新的VBox(10);left.setMinWidth(200);右=新的VBox(10);right.setMinWidth(200);HBox hbox =新的HBox(10,左,右);pausePlay = new Button();按钮重置=新按钮(重置");reset.setOnAction(e-> reset());重启();BorderPane根=新的BorderPane(hbox);HBox按钮=新的HBox(5,暂停播放,重置);button.setAlignment(Pos.CENTER);root.setBottom(buttons);场景=新场景(root,600,600);primaryStage.setScene(scene);primaryStage.show();}私人无效reset(){如果(时间轴!= 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));}时间轴=新时间轴(新KeyFrame(Duration.seconds(1),e-> {如果(moreStepsToDo()){doNextStep();} 别的 {timeline.stop();}}));timeline.setCycleCount(Animation.INDEFINITE);pausePlay.disableProperty().bind(Bindings.createBooleanBinding(()-> {如果(left.getChildren().isEmpty()){返回true;}返回false;},left.getChildren()));pausePlay.textProperty().bind(Bindings.createStringBinding(()-> {如果(timeline.getStatus()== Animation.Status.RUNNING){返回暂停";}返回播放";},timeline.statusProperty()));pausePlay.setOnAction(e-> {如果(timeline.getStatus()== Animation.Status.RUNNING){timeline.pause();} 别的 {timeline.play();}});}私人布尔moreStepsToDo(){返回!left.getChildren().isEmpty();}私人void doNextStep(){int n = left.getChildren().size();节点node = left.getChildren().remove(n-1);right.getChildren().add(node);}公共静态void main(String [] args){发射(参数);}} 

如果您确实想对线程执行此操作,则暂停线程的一种方法是使用documentation but I still don't get it. There's not similar example :(

The problem is how to do a stack and queue simulator.

  1. Generate 10 random numbers. Done.
  2. Show the numbers in a table. Done.
  3. Use the 10 random numbers to simulate a stack and a queue. I don't now how to comunicate the service with the TextField.
  4. Pause the simulation. or Stop.

-The program needs a pause method. I don't know how to pause a thead. Perhaps with wait() and notify(). I don't know.

I have used label.textProperty.bind(service.progressProperty()). this works but when i try to bind a variable instead the method updateProgress(i,n) throws a exception.

Maybe I need to use 2 Tasks.

Main class:

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);
}
}

Controller Class:

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);
  }

}

Service class:

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;
      }
    };
  }
}

解决方案

There's a lot of code in the question, and I don't think you need it all to address the concepts you're actually asking about. So I'll just give a high-level answer here. If you want to edit your question to something much simpler that addresses the actual issue, then I can make this specific to that example.

I would probably try to do this without threads at all, but using the animation API. For example, you could use a Timeline, with the following basic outline:

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);
    }
}

The nice thing about this solution is that everything is single-threaded: the event handler for the key frame is executed on the FX Application Thread, which is the same thread that executes the event handlers. This means there is no need to worry about synchronizing data across threads. The predefined methods in the animation API pause(), play(), and stop() provide exactly the functionality you're looking for; you just have to update the application state appropriately.

Here's a simple complete example that uses this approach (it just moves a bunch of rectangles, one at a time, from one vbox to another).

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);
    }
}

If you do want to do this with threads, one approach to pausing a thread is to use a Semaphore with a single permit. This generally looks something like this:

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...
    }
});

(Obviously the same idiom will work in your Task, which is executed on a background thread.)

Then calling pauser.acquire(); from the controller will pause the simulation (because the simulation thread will not be able to acquire the permit), and calling pauser.release() while it is paused will let it run again.

这篇关于如何使用暂停,线程(任务,服务)模拟队列和堆栈JavaFX的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆