JavaFX UI冻结问题

时间:2012-12-12 14:10:56

标签: java multithreading concurrency javafx-2

我试图展示每项任务的进度和状态。每个任务代表一行TableView。每个任务在每个Thread中并行执行。请参考TableView的图片。

enter image description here

对于“进度”TableColumn我已将Cell Factory设置为渲染“ProgressBar”。

  public static class ProgressBarTableCell<S, T> extends TableCell<S, T> {

        private final ProgressBar progressBar;
        private ObservableValue<T> ov;

        public ProgressBarTableCell() {
            this.progressBar = new ProgressBar();
            progressBar.setPrefHeight(23);
            setAlignment(Pos.CENTER);
        }

        @Override
        public void updateItem(T item, boolean empty) {
            super.updateItem(item, empty);
            if (item == null) {
                setGraphic(null);
                setText(null);
            } else {
                if (item.toString().equalsIgnoreCase("Processing")) {
                    Platform.runLater(new Runnable() {
                        @Override
                        public void run() {

                            if (getGraphic() == null) {
                                setGraphic(progressBar);
                                progressBar.setProgress(-1);
                            } else {
                                ProgressBar objpProgressBar = (ProgressBar) getGraphic();
                                objpProgressBar.setProgress(-1);
                            }
                            setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
                        }
                    });
                } else {
                    Platform.runLater(new Runnable() {
                        @Override
                        public void run() {
                            if (getGraphic() == null) {
                                setGraphic(progressBar);
                                progressBar.setProgress(0);
                            } else {
                                ProgressBar objpProgressBar = (ProgressBar) getGraphic();
                                objpProgressBar.setProgress(0);
                            }
                            setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
                        }
                    });
                }
            }
        }
    } 

TableView没有显示更新状态和任务进度,所以我在后台定期运行一个帖子,当我调用 startUpdatingTableProgress()时修改TableView当我打电话给 stopUpdatingTableProgress()时停止。

 public void stopUpdatingTableProgress() {
    keepUpdating = false;
}

public void startUpdatingTableProgress() {
    keepUpdating = true;
    TableProgressBarUpdator tpu = new TableProgressBarUpdator(table);
    tpu.start();
}

class TableProgressBarUpdator implements Runnable {

    TableView table;

    public TableProgressBarUpdator(TableView fxtable) {
        table = fxtable;

    }

    public void start() {
        new Thread(this).start();
    }

    public void run() {

        while (keepUpdating) {
            try {
                updateProgressbar();
                Thread.sleep(1000);
            } catch (Exception e) {
                LogHandler.doErrorLogging("Error while updating tables cell", e);
            }
        }
        LogHandler.doDebugLogging("Table process repainting is completed.");
    }

    private void updateProgressbar() throws Exception {
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                ((TableColumn) table.getColumns().get(0)).setVisible(false);
                ((TableColumn) table.getColumns().get(0)).setVisible(true);
            }
        });


    }
}

但是现在我的JavaFX应用程序的UI在连续几次后冻结。有没有其他方法可以在后台线程中调用 updateProgressbar(),并在特定时间间隔后自动触发并且不冻结我的JavaFX应用程序UI?

我不知道我是否正确地更新每个任务在单独线程中运行的任务状态。如果我想在javafx.concurrent package的帮助下实现这一点,那么有人可以向我展示一些指导或流程吗?什么将进入任务,什么将进入服务类?我如何安排它来更新进度条?仅供参考:一次只允许5个线程并行执行。

已编辑 -

根据Andy Till先生的建议,我已经更新了我的代码,但仍然没有运气 -

TableColumn tableColumn_Progress = new TableColumn("Progress");
        tableColumn_Progress.setCellValueFactory(new PropertyValueFactory<QueueData, Double>("progressBar"));
        tableColumn_Progress.setPrefWidth(100);

        Callback<TableColumn<QueueData, Double>, TableCell<QueueData, Double>> attachmentCellFactory = //
                new Callback<TableColumn<QueueData, Double>, TableCell<QueueData, Double>>() {
            @Override
            public TableCell call(final TableColumn param) {
                final ProgressBarTableCell cell = new ProgressBarTableCell() {
                    @Override
                    public void updateItem(Object item, boolean empty) {
                        super.updateItem(item, empty);
                    }
                };
                return cell;
            }
        };
        tableColumn_Progress.setCellFactory(attachmentCellFactory);

在QueueData类中 -

DoubleProperty progressBar = null;

public DoubleProperty progressBarProperty() {
        return progressBar;
    }

    public Double getprogressBar() {
        return progressBar.get();
    }

    public void setprogressBar(Double sType) {
        this.progressBar = new SimpleDoubleProperty(sType);
    }

并没有调用任何后台线程来更新UI但没有运气。

2 个答案:

答案 0 :(得分:5)

据我了解您的代码,您正在翻转可见性以触发表格单元格上的updateItem方法?这意味着任何数量的线程都可能尝试每秒更改可见性并无休止地重新绘制。

我知道你已经完成了很多实现,但是JavaFX服务类已经实现了一个可以更新的进度功能,这比你现在的执行方式要简单得多。

拥有一个进程double属性会更加简单和有效,您可以从线程更新并将其绑定到ProgressBar.progressProperty(),这样一切都会自动更新。这就是Service类的工作方式,但很容易实现。

还要确保处理线程,这很容易检查。在eclipse中调试,在调试透视图中,它将显示线程列表,确保它不会无限增长。

为了安排Runnable checkout ScheduledExecutor类,您可以使用Executors.newScheduledThreadPool(numOfThreads)轻松创建它。

答案 1 :(得分:5)

你的'QueueData'类应该有设置现有进度的double属性的方法,似乎你每次都在setprogressBar(Double sType)方法中设置进度状态时创建新的SimpleDoubleProperty。

将此更新为 -

 public void setprogressBar(Double sType) {
        this.progressBar.set(sType);
    }

只需在开始时创建progressBar对象,然后更新现有对象。