【问题标题】:Replace a TableView with a ProgressIndicator within VBox JavaFX在 VBox JavaFX 中用 ProgressIndicator 替换 TableView
【发布时间】:2014-11-26 11:33:07
【问题描述】:

我有一个与某些数据相关联的 TableView,一旦我按下运行按钮,我就会对该数据执行一些处理。每行数据都在一个单独的线程中处理,当这些线程正在运行时,我想要一个 ProgressInducator 来替换其 vbox 中的表。

在附件代码中:

如果我停在哪里说“如果在这里停止工作” - 表被替换为 pi。 如果我继续等待线程加入 - 无需替换。 我错过了什么?

    runButton.setOnAction(
                new EventHandler<ActionEvent>() {
                    @Override
                    public void handle(final ActionEvent e) {
                        List<Thread> threadList = new ArrayList<Thread>();

                        int threadCounter = 0;
                        final ProgressIndicator pi = new ProgressIndicator(threadCounter);

                        vbox.getChildren().clear();
                        vbox.getChildren().addAll(pi);
                        for (ProductInTable product : data) {
                            Thread thread = new Thread(new Runnable() {
                                @Override
                                public void run() {
                                    try
                                    {
                                        product.calculate();
                                    } catch (IOException ioe) {
                                        ioe.printStackTrace();
                                    }
                                }
                            });
                            threadList.add(thread);
                            thread.start();
                        }

                        int x = threadList.size();

                        /** WORKS IF STOP HERE **/

                        // wait for all threads to end
                        for (Thread t : threadList) {
                            try {
                                t.join();
                                threadCounter++;
                                pi.setProgress(threadCounter / x);
                            } catch (InterruptedException interE) {
                                interE.printStackTrace();
                            }
                        }

                        /** DOESNT WORKS IF STOP HERE **/

【问题讨论】:

    标签: multithreading javafx tableview progress-indicator


    【解决方案1】:

    Thread.join() 阻止执行,直到线程完成。由于您在 FX 应用程序线程上调用它,因此您会阻塞该线程,直到所有工作线程完成。这意味着在这些线程完成之前,UI 无法更新。

    更好的方法可能是用一个任务来表示每个计算,并使用setOnSucceeded 在 FX 应用程序线程上更新完成任务的计数器。比如:

    runButton.setOnAction(
                new EventHandler<ActionEvent>() {
                    @Override
                    public void handle(final ActionEvent e) {
    
                        final ProgressIndicator pi = new ProgressIndicator(threadCounter);
    
                        vbox.getChildren().clear();
                        vbox.getChildren().addAll(pi);
    
                        final int numTasks = data.size();
    
                        // only access from FX Application thread:
                        final IntegerProperty completedTaskCount = new SimpleIntegerProperty(0);
    
                        pi.progressProperty().bind(completedTaskCount.divide(1.0*numTasks));
    
                        completedTaskCount.addListener(new ChangeListener<Number>() {
                            @Override
                            public void changed(ObservableValue<? extends Number> obs, Number oldValue, Number newValue) { 
                                if (newValue.intValue() >= numTasks) {
                                    // hide progress indicator and show table..
                                }
                            }
                        });
    
                        for (final ProductInTable product : data) {
                            Task<Void> task = new Task<Void>() {
                                @Override
                                public Void call() {
                                    try
                                    {
                                        product.calculate();
                                    } catch (IOException ioe) {
                                        ioe.printStackTrace();
                                    }
                                    return null ;
                                }
                            });
                            task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
                                @Override
                                public void handle(WorkerStateEvent event) {
                                    completedTaskCount.set(completedTaskCount.get()+1);
                                }
                            });
                            new Thread(task).start();
                        }
    
                    }
    });
    

    如果您可能在这里有大量项目,您应该使用某种ExecutorService 来避免创建过多线程:

    ExecutorService exec = Executors.newFixedThreadPool(
        Runtime.getRuntime().availableProcessors()); // for example...
    

    然后替换

    new Thread(task).start();
    

    exec.submit(task);
    

    【讨论】:

    • 这取决于平台,我对这个问题没有很好的答案。我想我总是尽量保持与系统上处理器数量相同的数量级;但我不确定这是否是一个好政策。如果您有疑问,我建议使用ExecutorService 来管理日程安排。
    猜你喜欢
    • 2023-03-13
    • 1970-01-01
    • 2017-08-13
    • 2018-04-06
    • 2014-12-14
    • 1970-01-01
    • 2016-11-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多