Wat*_*ter 6 java multithreading javafx
我有一个选项,用户可以从FileChooser提交多个文件,以便由某些代码处理.结果将是用于读取文件的IO,然后是对存储数据的实际繁重计算.允许用户选择多个文件,并且由于文件处理不依赖于所选择的任何其他文件,因此使用线程更容易处理此问题.
此外,用户需要有一个按钮列表,每个要取消的任务一个,以及"全部取消"按钮.因此,我必须考虑选择性地或集体地杀死一个或所有任务的能力.
最后一个要求是我不会让用户通过打开大量文件来阻塞系统.因此,我想到了一个线程池数量有限的线程池(让我们假装我将它设置为4,任意数字).
我不确定如何正确地设置这一切.我有我需要做的逻辑,但使用正确的类是我被困的地方.
我已经检查了这个资源,所以如果答案是某种方式,那么我就误读了这篇文章.
是否有任何JavaFX类可以帮助我解决这种情况?
如果没有,我将如何将任务与某种线程池混合?我是否必须创建自己的线程池,或者是否已经为我提供了一个?
我是否在一个包含我愿意允许用户的最大线程数的单个地方?
我更喜欢使用已经在Java库中的一个,因为我不是多线程专家,并且担心我可能做错了.由于线程错误似乎是这个星球调试上最邪恶的东西,我想非常努力,以确保我这样做尽可能正确.
如果没有办法做到这一点,我必须推出自己的实现,那么最好的方法是做什么?
编辑:我应该注意,我通常是线程的新手,我之前使用过它们并且我正在阅读它们的书籍,但这将是我对它们的第一次主要用途,我真的很想做到这一点.
Jam*_*s_D 12
JavaFX有一个javafx.concurrentAPI; 特别是,Task该类非常适合您的用例.此API旨在与java.util.concurrentAPI 结合使用.例如,Task是一个实现FutureTask,所以它可以提交给Executor.当您想使用线程池时,您可以创建一个Executor为您实现线程池,并将任务提交给它:
final int MAX_THREADS = 4 ;
Executor exec = Executors.newFixedThreadPool(MAX_THREADS);
Run Code Online (Sandbox Code Playgroud)
由于这些线程在UI应用程序的后台运行,您可能不希望它们阻止应用程序退出.您可以通过执行执行程序守护程序线程创建的线程来实现此目的:
Executor exec = Executors.newFixedThreadPool(MAX_THREADS, runnable -> {
Thread t = new Thread(runnable);
t.setDaemon(true);
return t ;
});
Run Code Online (Sandbox Code Playgroud)
生成的执行程序将具有最多MAX_THREADS线程池.如果在没有线程可用时提交任务,它们将在队列中等待,直到线程可用.
要实现实际Task,需要记住以下几点:
您不能从后台线程更新UI.由于您Task已提交给上面的执行程序,因此call()将在后台线程上调用它的方法.如果您确实需要在执行call方法期间更改UI,则可以包装更改UI的代码Platform.runLater(...),但最好是对事物进行结构化以避免这种情况.特别是,它Task有一组updateXXX(...)方法可以更改TaskFX Application线程上相应属性的值.您的UI元素可以根据需要绑定到这些属性.
建议该call方法不要访问任何共享数据(除了通过上述updateXXX(...)方法之外).实例化您的Task子类设置仅final变量,让call()方法计算值,并返回值.
为了取消Task,Task该类定义了一个内置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);
}
}
Run Code Online (Sandbox Code Playgroud)