使用 JavaFX 中的任务数组列表执行并等待多个并行和顺序任务
Execute and wait for multiple parallel and sequential Tasks by using a Arraylist of Tasks in JavaFX
我正在寻找一种合适的方法来在单独的阶段显示并行 运行ning 任务的处理时间。
我想执行组合在 ArrayList 中的不同任务 - 一个接一个。对于这种情况,我使用的是 ThreadPool。在每个执行列表之后,我想等到所有任务完成。只有当任务达到“成功”状态时,我才想在主线程中做一些事情。之后,我想执行另一个任务列表,并在单独的舞台上将它们可视化。下图显示了所需的处理顺序(取决于下面列出的源代码):
enter image description here
为此我编写了 classes MyLoader。 MyLoader-class 包含一个单独的 Task 并将进度属性与构造函数中的 Label 和 Progressbar 绑定:
public class MyLoader {
public Label label = null;
public ProgressBar progressBar = null;
public VBox vbox;
public Task<Integer> task = null;
public String name;
public MyLoader(String name) {
this.name = name;
this.label = new Label();
this.progressBar = new ProgressBar();
this.vbox = new VBox(2);
//UI-Layout for Progress
this.vbox.getChildren().addAll(this.label, this.progressBar);
HBox.setHgrow(this.vbox, Priority.ALWAYS);
this.vbox.setAlignment(Pos.CENTER);
this.progressBar.prefWidthProperty().bind(this.vbox.widthProperty().subtract(20));
//Counter-Size
Random r = new Random();
int max = r.nextInt((100 - 50) + 1) + 50;
//Task
this.task = new Task<Integer>() {
@Override
protected Integer call() throws Exception {
int idx = 0;
while(idx <= max) {
Thread.sleep(20); //... for long lasting processes
updateMessage(name+"-progress: "+idx);
updateProgress(idx, max);
idx++;
}
return max;
}
protected void succeeded() {
updateMessage(name+" succeeded!");
System.out.println(name+" succeeded!");
super.succeeded();
}
};
//Bind Properties
this.label.textProperty().bind(task.messageProperty());
this.progressBar.progressProperty().bind(task.progressProperty());
}
}
在 MainClass 中,我将几个 MyLoader 实例组合在一个 ArrayList 中,运行 它们与一个 ExecutorService。要创建新阶段,我使用静态方法 progressStage(List)。每个阶段都在 ExecutorService 执行相应任务之前显示。这是 MainClass 代码:
public class MainClass extends Application{
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
//Thread-Pool
ExecutorService es = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
//FirstLoaders
List<MyLoader> firstLoaders = new ArrayList<MyLoader>();
firstLoaders.add(new MyLoader("A"));
firstLoaders.add(new MyLoader("B"));
//Show 1. Stage
Stage firstStage = progressStage(firstLoaders);
firstStage.show();
//Execute firstLoaders
for(MyLoader l1 : firstLoaders)
es.execute(l1.task);
//1) TODO: How can I wait for the completion of the first loaders and start the second loaders?
//... doSomething1() ...
//SecondLoaders
List<MyLoader> secondLoaders = new ArrayList<MyLoader>();
secondLoaders.add(new MyLoader("C"));
secondLoaders.add(new MyLoader("D"));
secondLoaders.add(new MyLoader("E"));
//Show 2. Stage
Stage secondStage = progressStage(secondLoaders);
secondStage.setX(firstStage.getX());
secondStage.setY(firstStage.getY()+firstStage.getHeight());
secondStage.show();
for(MyLoader l2 : secondLoaders)
es.execute(l2.task);
//2) TODO How can I wait for the completion of the second loaders and start the primaryStage?
//... doSomething2() ...
Scene scene = new Scene(new StackPane(), 450, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
static Stage progressStage(List<MyLoader> loaderTasks) {
int count = loaderTasks.size();
VBox loadBox = new VBox(count);
for(int i=0; i<count; i++)
loadBox.getChildren().add(loaderTasks.get(i).vbox);
HBox.setHgrow(loadBox, Priority.ALWAYS);
loadBox.setAlignment(Pos.CENTER);
Stage dialogStage = new Stage();
dialogStage.setScene(new Scene(loadBox, 300, count * 50));
dialogStage.setAlwaysOnTop(true);
return dialogStage;
}
}
该程序到目前为止是可执行的 - 但计算序列似乎完全并行。
我尝过的:
1) 到目前为止,我已经设法使用 get() 方法读取和停止进程。但是只有当后台线程完成工作时,舞台才会显示。
//1) TODO: „doSomeThing1()“
List<Integer> integers = new ArrayList<Integer>();
for(MyLoader ml : firstLoaders)
integers.add(ml.task.get());
System.out.println(integers.toString());
2) 同样使用 Task.setOnSucceded() 方法我还不能得到任何有用的结果。主要是因为stage是在计算之后才显示出来的。问题是我无法在定义的时间查询所有任务的状态。
3) CountDownLatch的应用也取得了类似的效果
4) 此外,ExecutorService 的shutdown() 方法导致终止。因此该方案也不适用。
//1) TODO: „doSomeThing1()“
es.shutdown();
try {
es.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
//SecondLoaders
//...
}catch (InterruptedException e) {
e.printStackTrace();
}
是否有适合这种意图的方法?到目前为止,我还没有得出任何有用的结果。
任务完成后,只需更新计数器并检查当前完成的任务是否是当前集合中的最后一个。
以下代码演示了这一点。 (虽然代码中肯定有一些可以改进的地方,但概念应该清楚。)
public class App extends Application {
public static void main(String[] args) {
launch(args);
}
private VBox taskViewContainer;
ExecutorService executor;
int tasksDone;
private void runTasks(List<MyTask> tasks, IntegerProperty index) {
if (tasks.isEmpty()) {
index.set(index.get()+1);
} else {
int taskCount = tasks.size();
tasksDone = 0;
for (MyTask task : tasks) {
taskViewContainer.getChildren().add(new TaskView(task));
task.setOnSucceeded(evt -> {
++tasksDone;
if (tasksDone == taskCount) {
// proceed to next task set after all tasks are done
index.set(index.get() + 1);
}
});
executor.submit(task);
}
}
}
@Override
public void init() throws Exception {
// create executor during initialisation
executor = Executors.newFixedThreadPool(4);
}
@Override
public void stop() throws Exception {
// shutdown executor when javafx shuts down
executor.shutdownNow();
}
@Override
public void start(Stage primaryStage) throws Exception {
taskViewContainer = new VBox();
Label text = new Label();
// generate random set of tasks
Random random = new Random();
List<List<MyTask>> taskLists = new ArrayList<>();
for (int i = 0; i < 20; ++i) {
int count = random.nextInt(10) + 1;
List<MyTask> tasks = new ArrayList<>(count);
taskLists.add(tasks);
for (int j = 0; j < count; ++j) {
tasks.add(new MyTask(String.format("%d.%c", i+1, (char) ('A'+j)), random.nextInt((100 - 50) + 1) + 50));
}
}
// property holding the current index in the task set list
IntegerProperty index = new SimpleIntegerProperty(-1);
index.addListener((o, oldValue, newValue) -> {
// gui update for change of task set
taskViewContainer.getChildren().clear();
text.setText(String.format("Task set %d / %d done", newValue, taskLists.size()));
int i = newValue.intValue();
if (i < taskLists.size()) {
// launch next set of tasks
runTasks(taskLists.get(i), index);
}
});
// start initial tasks
index.set(0);
text.setMinWidth(200);
text.setMaxWidth(Double.MAX_VALUE);
HBox root = new HBox(text, taskViewContainer);
root.setMinHeight(10 * 50);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
}
class TaskView extends HBox {
TaskView(MyTask task) {
setPrefSize(400, 50);
ProgressBar progress = new ProgressBar();
progress.progressProperty().bind(task.progressProperty());
Label label = new Label(task.getName());
Label message = new Label();
message.textProperty().bind(task.messageProperty());
getChildren().addAll(progress, new VBox(label, message));
}
}
class MyTask extends Task<Integer> {
private final int max;
private final String name;
public String getName() {
return name;
}
public MyTask(String name, int max) {
this.max = max;
this.name = name;
}
@Override
protected Integer call() throws Exception {
int idx = 0;
while(idx <= max) {
Thread.sleep(20); //... for long lasting processes
updateMessage(name+"-progress: "+idx);
updateProgress(idx, max);
idx++;
}
return max;
}
}
以上代码不考虑取消tasks/tasks异常终止的可能性
我正在寻找一种合适的方法来在单独的阶段显示并行 运行ning 任务的处理时间。
我想执行组合在 ArrayList 中的不同任务 - 一个接一个。对于这种情况,我使用的是 ThreadPool。在每个执行列表之后,我想等到所有任务完成。只有当任务达到“成功”状态时,我才想在主线程中做一些事情。之后,我想执行另一个任务列表,并在单独的舞台上将它们可视化。下图显示了所需的处理顺序(取决于下面列出的源代码): enter image description here
为此我编写了 classes MyLoader。 MyLoader-class 包含一个单独的 Task 并将进度属性与构造函数中的 Label 和 Progressbar 绑定:
public class MyLoader {
public Label label = null;
public ProgressBar progressBar = null;
public VBox vbox;
public Task<Integer> task = null;
public String name;
public MyLoader(String name) {
this.name = name;
this.label = new Label();
this.progressBar = new ProgressBar();
this.vbox = new VBox(2);
//UI-Layout for Progress
this.vbox.getChildren().addAll(this.label, this.progressBar);
HBox.setHgrow(this.vbox, Priority.ALWAYS);
this.vbox.setAlignment(Pos.CENTER);
this.progressBar.prefWidthProperty().bind(this.vbox.widthProperty().subtract(20));
//Counter-Size
Random r = new Random();
int max = r.nextInt((100 - 50) + 1) + 50;
//Task
this.task = new Task<Integer>() {
@Override
protected Integer call() throws Exception {
int idx = 0;
while(idx <= max) {
Thread.sleep(20); //... for long lasting processes
updateMessage(name+"-progress: "+idx);
updateProgress(idx, max);
idx++;
}
return max;
}
protected void succeeded() {
updateMessage(name+" succeeded!");
System.out.println(name+" succeeded!");
super.succeeded();
}
};
//Bind Properties
this.label.textProperty().bind(task.messageProperty());
this.progressBar.progressProperty().bind(task.progressProperty());
}
}
在 MainClass 中,我将几个 MyLoader 实例组合在一个 ArrayList 中,运行 它们与一个 ExecutorService。要创建新阶段,我使用静态方法 progressStage(List)。每个阶段都在 ExecutorService 执行相应任务之前显示。这是 MainClass 代码:
public class MainClass extends Application{
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
//Thread-Pool
ExecutorService es = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
//FirstLoaders
List<MyLoader> firstLoaders = new ArrayList<MyLoader>();
firstLoaders.add(new MyLoader("A"));
firstLoaders.add(new MyLoader("B"));
//Show 1. Stage
Stage firstStage = progressStage(firstLoaders);
firstStage.show();
//Execute firstLoaders
for(MyLoader l1 : firstLoaders)
es.execute(l1.task);
//1) TODO: How can I wait for the completion of the first loaders and start the second loaders?
//... doSomething1() ...
//SecondLoaders
List<MyLoader> secondLoaders = new ArrayList<MyLoader>();
secondLoaders.add(new MyLoader("C"));
secondLoaders.add(new MyLoader("D"));
secondLoaders.add(new MyLoader("E"));
//Show 2. Stage
Stage secondStage = progressStage(secondLoaders);
secondStage.setX(firstStage.getX());
secondStage.setY(firstStage.getY()+firstStage.getHeight());
secondStage.show();
for(MyLoader l2 : secondLoaders)
es.execute(l2.task);
//2) TODO How can I wait for the completion of the second loaders and start the primaryStage?
//... doSomething2() ...
Scene scene = new Scene(new StackPane(), 450, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
static Stage progressStage(List<MyLoader> loaderTasks) {
int count = loaderTasks.size();
VBox loadBox = new VBox(count);
for(int i=0; i<count; i++)
loadBox.getChildren().add(loaderTasks.get(i).vbox);
HBox.setHgrow(loadBox, Priority.ALWAYS);
loadBox.setAlignment(Pos.CENTER);
Stage dialogStage = new Stage();
dialogStage.setScene(new Scene(loadBox, 300, count * 50));
dialogStage.setAlwaysOnTop(true);
return dialogStage;
}
}
该程序到目前为止是可执行的 - 但计算序列似乎完全并行。
我尝过的:
1) 到目前为止,我已经设法使用 get() 方法读取和停止进程。但是只有当后台线程完成工作时,舞台才会显示。
//1) TODO: „doSomeThing1()“
List<Integer> integers = new ArrayList<Integer>();
for(MyLoader ml : firstLoaders)
integers.add(ml.task.get());
System.out.println(integers.toString());
2) 同样使用 Task.setOnSucceded() 方法我还不能得到任何有用的结果。主要是因为stage是在计算之后才显示出来的。问题是我无法在定义的时间查询所有任务的状态。
3) CountDownLatch的应用也取得了类似的效果
4) 此外,ExecutorService 的shutdown() 方法导致终止。因此该方案也不适用。
//1) TODO: „doSomeThing1()“
es.shutdown();
try {
es.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
//SecondLoaders
//...
}catch (InterruptedException e) {
e.printStackTrace();
}
是否有适合这种意图的方法?到目前为止,我还没有得出任何有用的结果。
任务完成后,只需更新计数器并检查当前完成的任务是否是当前集合中的最后一个。
以下代码演示了这一点。 (虽然代码中肯定有一些可以改进的地方,但概念应该清楚。)
public class App extends Application {
public static void main(String[] args) {
launch(args);
}
private VBox taskViewContainer;
ExecutorService executor;
int tasksDone;
private void runTasks(List<MyTask> tasks, IntegerProperty index) {
if (tasks.isEmpty()) {
index.set(index.get()+1);
} else {
int taskCount = tasks.size();
tasksDone = 0;
for (MyTask task : tasks) {
taskViewContainer.getChildren().add(new TaskView(task));
task.setOnSucceeded(evt -> {
++tasksDone;
if (tasksDone == taskCount) {
// proceed to next task set after all tasks are done
index.set(index.get() + 1);
}
});
executor.submit(task);
}
}
}
@Override
public void init() throws Exception {
// create executor during initialisation
executor = Executors.newFixedThreadPool(4);
}
@Override
public void stop() throws Exception {
// shutdown executor when javafx shuts down
executor.shutdownNow();
}
@Override
public void start(Stage primaryStage) throws Exception {
taskViewContainer = new VBox();
Label text = new Label();
// generate random set of tasks
Random random = new Random();
List<List<MyTask>> taskLists = new ArrayList<>();
for (int i = 0; i < 20; ++i) {
int count = random.nextInt(10) + 1;
List<MyTask> tasks = new ArrayList<>(count);
taskLists.add(tasks);
for (int j = 0; j < count; ++j) {
tasks.add(new MyTask(String.format("%d.%c", i+1, (char) ('A'+j)), random.nextInt((100 - 50) + 1) + 50));
}
}
// property holding the current index in the task set list
IntegerProperty index = new SimpleIntegerProperty(-1);
index.addListener((o, oldValue, newValue) -> {
// gui update for change of task set
taskViewContainer.getChildren().clear();
text.setText(String.format("Task set %d / %d done", newValue, taskLists.size()));
int i = newValue.intValue();
if (i < taskLists.size()) {
// launch next set of tasks
runTasks(taskLists.get(i), index);
}
});
// start initial tasks
index.set(0);
text.setMinWidth(200);
text.setMaxWidth(Double.MAX_VALUE);
HBox root = new HBox(text, taskViewContainer);
root.setMinHeight(10 * 50);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
}
class TaskView extends HBox {
TaskView(MyTask task) {
setPrefSize(400, 50);
ProgressBar progress = new ProgressBar();
progress.progressProperty().bind(task.progressProperty());
Label label = new Label(task.getName());
Label message = new Label();
message.textProperty().bind(task.messageProperty());
getChildren().addAll(progress, new VBox(label, message));
}
}
class MyTask extends Task<Integer> {
private final int max;
private final String name;
public String getName() {
return name;
}
public MyTask(String name, int max) {
this.max = max;
this.name = name;
}
@Override
protected Integer call() throws Exception {
int idx = 0;
while(idx <= max) {
Thread.sleep(20); //... for long lasting processes
updateMessage(name+"-progress: "+idx);
updateProgress(idx, max);
idx++;
}
return max;
}
}
以上代码不考虑取消tasks/tasks异常终止的可能性