使用JavaFX Tasks正确执行多线程和线程池

我可以选择让用户从FileChooser提交多个文件,以通过一些代码进行处理。结果将是读取文件的IO,然后是对存储数据的实际大量计算。允许用户选择多个文件,并且由于文件处理不依赖于所选的任何其他文件,因此使我的工作变得更加轻松。

此外,用户需要具有按钮列表,每个要取消的任务一个按钮以及“全部取消”按钮。因此,我必须考虑选择性或集体杀死一项或所有任务的能力。

最后一个要求是,我不允许用户通过打开大量文件来阻塞系统。因此,我认为线程池中的线程数有限(假装我将其限制为4个任意数目)。

我不确定如何正确地进行所有设置。我有我需要做的逻辑,但是使用正确的类才是我受困的地方。

我已经检查了此资源,所以如果答案在那里,那么我会误读本文。

  • 是否有任何JavaFX类可以帮助我解决这种情况?

  • 如果没有,我如何将Task与某种线程池混合使用?我必须建立自己的线程池,还是已经为我提供了一个?

  • 我是否要在某个地方创建一个单例,其中包含我愿意允许用户的最大线程数?

我宁愿使用Java库中已经存在的Java库,因为我不是多线程专家,并且担心自己可能做错了。由于线程错误似乎是这个星球调试上最邪恶的东西,我想

努力,以确保我这样做尽可能正确。

如果没有办法做到这一点,而我必须自己实施,那么最好的方法是什么?

编辑:我应该注意,我通常是线程的新手,我以前使用过它们,并且正在阅读有关它们的书,但这将是我对线程的第一个主要用途,我真的很想正确地做。

回答:

JavaFX有一个javafx.concurrentAPI;特别是,Task该类非常适合您的用例。该API旨在与java.util.concurrentAPI

结合使用。例如,Task是的实现FutureTask,因此可以将其提交给Executor。当您想使用线程池时,可以创建一个Executor为您实现线程池的对象,然后向其中提交任务:

final int MAX_THREADS = 4 ;

Executor exec = Executors.newFixedThreadPool(MAX_THREADS);

由于这些线程在UI应用程序的后台运行,因此您可能不希望它们阻止应用程序退出。您可以通过使执行程序守护程序线程创建的线程来实现此目的:

Executor exec = Executors.newFixedThreadPool(MAX_THREADS, runnable -> {

Thread t = new Thread(runnable);

t.setDaemon(true);

return t ;

});

生成的执行程序将具有最多MAX_THREADS线程池。如果任务在没有可用线程的情况下提交,则它们将在队列中等待直到线程可用。

要实现实际的Task,需要牢记以下几点:

不得

从后台线程更新UI。由于您Task的提交给了上面的执行者,因此call()它将在后台线程上调用它的方法。如果确实需要在call方法执行期间更改UI,则可以在中包装用于更改UI的代码Platform.runLater(...),但是最好对结构进行结构化,以免发生这种情况。特别是,Task具有一组updateXXX(...)方法来更改TaskFX

Application线程上相应属性的值。您的UI元素可以根据需要绑定到这些属性。

建议该call方法不要访问任何共享数据(通过上述updateXXX(...)方法除外)。实例化Task仅设置final变量的子类,让该call()方法计算一个值,然后返回该值。

为了取消TaskTask该类定义了一个内置cancel()方法。如果您使用的call()方法运行时间较长,则应定期检查的值,isCancelled()如果返回则停止工作true

这是一个基本示例:

import java.io.File;

import java.util.Arrays;

import java.util.List;

import java.util.Random;

import java.util.concurrent.Executor;

import java.util.concurrent.Executors;

import javafx.application.Application;

import javafx.beans.property.ReadOnlyObjectWrapper;

import javafx.beans.value.ChangeListener;

import javafx.concurrent.Task;

import javafx.concurrent.Worker;

import javafx.geometry.Insets;

import javafx.geometry.Pos;

import javafx.scene.Scene;

import javafx.scene.control.Button;

import javafx.scene.control.TableCell;

import javafx.scene.control.TableColumn;

import javafx.scene.control.TableView;

import javafx.scene.control.cell.ProgressBarTableCell;

import javafx.scene.layout.BorderPane;

import javafx.scene.layout.HBox;

import javafx.stage.FileChooser;

import javafx.stage.Stage;

public class FileTaskExample extends Application {

private static final Random RNG = new Random();

private static final int MAX_THREADS = 4 ;

private final Executor exec = Executors.newFixedThreadPool(MAX_THREADS, runnable -> {

Thread t = new Thread(runnable);

t.setDaemon(true);

return t ;

});

@Override

public void start(Stage primaryStage) {

// table to display all tasks:

TableView<FileProcessingTask> table = new TableView<>();

TableColumn<FileProcessingTask, File> fileColumn = new TableColumn<>("File");

fileColumn.setCellValueFactory(cellData -> new ReadOnlyObjectWrapper<File>(cellData.getValue().getFile()));

fileColumn.setCellFactory(col -> new TableCell<FileProcessingTask, File>() {

@Override

public void updateItem(File file, boolean empty) {

super.updateItem(file, empty);

if (empty) {

setText(null);

} else {

setText(file.getName());

}

}

});

fileColumn.setPrefWidth(200);

TableColumn<FileProcessingTask, Worker.State> statusColumn = new TableColumn<>("Status");

statusColumn.setCellValueFactory(cellData -> cellData.getValue().stateProperty());

statusColumn.setPrefWidth(100);

TableColumn<FileProcessingTask, Double> progressColumn = new TableColumn<>("Progress");

progressColumn.setCellValueFactory(cellData -> cellData.getValue().progressProperty().asObject());

progressColumn.setCellFactory(ProgressBarTableCell.forTableColumn());

progressColumn.setPrefWidth(100);

TableColumn<FileProcessingTask, Long> resultColumn = new TableColumn<>("Result");

resultColumn.setCellValueFactory(cellData -> cellData.getValue().valueProperty());

resultColumn.setPrefWidth(100);

TableColumn<FileProcessingTask, FileProcessingTask> cancelColumn = new TableColumn<>("Cancel");

cancelColumn.setCellValueFactory(cellData -> new ReadOnlyObjectWrapper<FileProcessingTask>(cellData.getValue()));

cancelColumn.setCellFactory(col -> {

TableCell<FileProcessingTask, FileProcessingTask> cell = new TableCell<>();

Button cancelButton = new Button("Cancel");

cancelButton.setOnAction(e -> cell.getItem().cancel());

// listener for disabling button if task is not running:

ChangeListener<Boolean> disableListener = (obs, wasRunning, isNowRunning) ->

cancelButton.setDisable(! isNowRunning);

cell.itemProperty().addListener((obs, oldTask, newTask) -> {

if (oldTask != null) {

oldTask.runningProperty().removeListener(disableListener);

}

if (newTask == null) {

cell.setGraphic(null);

} else {

cell.setGraphic(cancelButton);

cancelButton.setDisable(! newTask.isRunning());

newTask.runningProperty().addListener(disableListener);

}

});

return cell ;

});

cancelColumn.setPrefWidth(100);

table.getColumns().addAll(Arrays.asList(fileColumn, statusColumn, progressColumn, resultColumn, cancelColumn));

Button cancelAllButton = new Button("Cancel All");

cancelAllButton.setOnAction(e ->

table.getItems().stream().filter(Task::isRunning).forEach(Task::cancel));

Button newTasksButton = new Button("Process files");

FileChooser chooser = new FileChooser();

newTasksButton.setOnAction(e -> {

List<File> files = chooser.showOpenMultipleDialog(primaryStage);

if (files != null) {

files.stream().map(FileProcessingTask::new).peek(exec::execute).forEach(table.getItems()::add);

}

});

HBox controls = new HBox(5, newTasksButton, cancelAllButton);

controls.setAlignment(Pos.CENTER);

controls.setPadding(new Insets(10));

BorderPane root = new BorderPane(table, null, null, controls, null);

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

primaryStage.setScene(scene);

primaryStage.show();

}

public static class FileProcessingTask extends Task<Long> {

private final File file ;

public FileProcessingTask(File file) {

this.file = file ;

}

public File getFile() {

return file ;

}

@Override

public Long call() throws Exception {

// just to show you can return the result of the computation:

long fileLength = file.length();

// dummy processing, in real life read file and do something with it:

int delay = RNG.nextInt(50) + 50 ;

for (int i = 0 ; i < 100; i++) {

Thread.sleep(delay);

updateProgress(i, 100);

// check for cancellation and bail if cancelled:

if (isCancelled()) {

updateProgress(0, 100);

break ;

}

}

return fileLength ;

}

}

public static void main(String[] args) {

launch(args);

}

}

以上是 使用JavaFX Tasks正确执行多线程和线程池 的全部内容, 来源链接: utcz.com/qa/432269.html

回到顶部