0

I have a TableView associated with some data, and once i hit a run button i perform some processing on that data. Each row of data is handled in a seperate thread, and while those threads are running i want a ProgressInducator to replace the table within its vbox.

In the attached code:

If I stop where is says "WORKS IF STOP HERE" - table is replaced with pi. If I continue waiting for the threads to join - no replacing. What am I missing?

    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 **/
4

1 回答 1

0

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

更好的方法可能是用一个任务来表示每个计算,并使用setOnSucceeded. 就像是:

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);
于 2014-10-01T15:37:52.850 回答