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

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

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

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

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

我已经检查了此资源,所以如果答案在某种程度上在那里,那我就误读了这篇文章。

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

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

  • 我是否要在某个地方包含我愿意允许用户的最大线程数的单身人士?

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

如果没有办法做到这一点,而我不得不推行自己的实施方式,那么这样做的最佳方法是什么?

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

詹姆斯·D

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);
    }
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

使用连接并正确处理中断的执行的Java多线程

来自分类Dev

异步任务和线程池执行器的使用

来自分类Dev

这个Ruby代码是否正确使用了线程,线程池和并发性

来自分类Dev

如何使用JavaFX进行多线程

来自分类Dev

JavaFX多线程和进度栏

来自分类Dev

JavaFX 中的 JUnit 和多线程

来自分类Dev

JavaFX中的多线程

来自分类Dev

JavaFX中的多线程

来自分类Dev

如何使用多线程执行子流程

来自分类Dev

在多线程程序中正确使用QSqlDatabase

来自分类Dev

在游戏中使用正确的多线程

来自分类Dev

使用asio增强线程池:随机不执行线程

来自分类Dev

如何确保线程被完全执行?(使用线程池)

来自分类Dev

使用Boost线程和io_service创建线程池

来自分类Dev

使用ThreadPool和CountdownEvent的多线程

来自分类Dev

使用Python和pymongo的多线程

来自分类Dev

在C ++中使用cURL和多线程

来自分类Dev

使用多线程处理和保存IMage

来自分类Dev

如何使用 pika 和 rabbitmq 的多线程来执行请求和响应 RPC 消息

来自分类Dev

使用线程池对for循环内完成的工作进行多线程处理

来自分类Dev

即使在使用线程池的情况下,多线程的许多短期任务也会变慢

来自分类Dev

多线程 JavaFX 用户界面

来自分类Dev

JavaFX Canvas多线程应使用哪种方法?

来自分类Dev

JavaFX 和多线程:ObservableList.add() 上的 IllegalStateException

来自分类Dev

异步使用线程池?

来自分类Dev

使用线程池执行任务时出现RejectedExecutionException:JAVA

来自分类Dev

Android使用多个线程池执行器

来自分类Dev

Java-何时使用固定大小的线程池和可变大小的线程池?

来自分类Dev

Java-何时使用固定大小的线程池和可变大小的线程池?

Related 相关文章

热门标签

归档