我可以选择让用户从FileChooser提交多个文件,以通过一些代码进行处理。结果将是读取文件的IO,然后是对存储数据的实际大量计算。允许用户选择多个文件,并且由于文件处理不依赖于所选的任何其他文件,因此使我的工作变得更加轻松。
此外,用户需要具有按钮列表,每个要取消的任务一个按钮以及“全部取消”按钮。因此,我必须考虑选择性或集体杀死一项或所有任务的能力。
最后一个要求是,我不允许用户通过打开大量文件来阻塞系统。因此,我认为线程池中的线程数有限(假装我将其限制为4个任意数目)。
我不确定如何正确地进行所有设置。我有我需要做的逻辑,但是使用正确的类才是我受困的地方。
我已经检查了此资源,所以如果答案在那里,那么我会误读本文。
是否有任何JavaFX类可以帮助我解决这种情况?
如果没有,我如何将Task与某种线程池混合使用?我必须建立自己的线程池,还是已经为我提供了一个?
我是否要在某个地方创建一个单例,其中包含我愿意允许用户的最大线程数?
我宁愿使用Java库中已经存在的Java库,因为我不是多线程专家,并且担心自己可能做错了。由于线程错误似乎是这个星球调试上最邪恶的东西,我想 非常
努力,以确保我这样做尽可能正确。
如果没有办法做到这一点,而我必须自己实施,那么最好的方法是什么?
编辑:我应该注意,我通常是线程的新手,我以前使用过它们,并且正在阅读有关它们的书,但这将是我对线程的第一个主要用途,我真的很想正确地做。
JavaFX有一个javafx.concurrent
API;特别是,Task
该类非常适合您的用例。该API旨在与java.util.concurrent
API
结合使用。例如,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(...)
方法来更改Task
FX
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);
}
}
6.7.2.多线程执行 与在单线程中阻塞相比,更好的做法是让程序运行在多个线程之中。系统负责分配CPU时间,几个线程仿佛在同一时刻同时运行。这样可以避免某线程独占计算资源。 图6.10. 多线程执行 在例子中,我们将网络操作的相关代码放到独立的线程里面。这样我们的主线程可以避免阻塞在网络操作上,用户界面不会响应不灵。按惯例,我们一般认为主线程是运行于前台,而其它的线程都是运行于后台。这是因为前端的
我们正在对使用SpringBoot 2.2.2和Spring执行器的应用程序进行性能测试。 我们希望监控: 正在使用多少tomcat线程 有多少tomcat请求正在排队 正在使用多少个ThreadPoolTaskExector线程(我们将@Async与线程池一起用于某些任务) 执行器中是否提供此信息?我看不到需要使用哪些指标。
问题内容: 下面的代码尝试做到这一点。 该代码将永远循环并检查是否有任何待处理的请求要处理。如果存在,它将创建一个新线程来处理请求并将其提交给执行者。一旦所有线程完成,它将休眠60秒,然后再次检查未决请求。 我的问题是这些线程完成的大多数处理都是针对数据库的。该程序将在Windows计算机上运行。当有人尝试关闭或注销计算机时,这些线程会发生什么?如何正常关闭正在运行的线程以及执行程序? 问题答案:
以下是问题陈述: 编写一个java程序,使用线程计算前25个素数,并计算前50个斐波那契数。将计算斐波那契数的线程的优先级设置为8,将另一个设置为5。在计算了30个斐波那契数之后,让这个线程进入睡眠状态,开始计算素数。计算完25个素数后,继续斐波那契数计算。 我的代码: 我本以为当斐波那契线停止时,其余的素数会被打印出来,但那没有发生,这背后的原因可能是什么?
问题内容: 我正在构建一个使用Spring Data和Hibernate的简单Tomcat Web应用程序。有一个端点要做很多工作,因此我想将工作卸载到后台线程,以便在完成工作时Web请求不会挂起10分钟以上。因此,我在一个组件扫描的程序包中编写了一个新服务: 然后在Spring中进行配置: 这一切都很好。但是,问题来自于Hibernate。在我的可运行内部,查询仅完成一半。我可以: 但是,如果我
我正在构建一个简单的Tomcat web应用程序,它使用Spring Data和Hibernate。有一个endpoint可以完成大量工作,因此我想将工作卸载到后台线程,以便在完成工作时 Web 请求不会挂起 10 分钟。所以我在组件扫描包中写了一个新的服务: 然后在Spring中配置: 这一切都很好。但是,问题来自于Hibernate。在我的runnable中,查询只完成了一半。我可以做: 但如