我一直在搜索YouTube,Stack-Overflow和fxexperience,甚至oracle documentation,但我仍然没有得到它。 这里没有类似的例子:(
问题是如何进行堆栈和队列模拟器。
- 程序需要暂停方法。我不知道如何暂停一个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;
}
};
}
}
答案 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>