JavaFX:ConcurrentModificationException同时在单独的线程中在TreeView中添加TreeItem对象

时间:2014-02-27 14:54:45

标签: treeview javafx concurrentmodification

我有以下代码

public void start(Stage primaryStage) {        
    BorderPane border_pane = new BorderPane();

    TreeView tree = addTreeView();                                          //TreeView on the LEFT
    border_pane.setLeft(tree);

    /* more stuff added to the border_pane here... */

    Scene scene = new Scene(border_pane, 900, 700);
    scene.setFill(Color.GHOSTWHITE);

    primaryStage.setTitle("PlugControl v0.1e");
    primaryStage.setScene(scene);
    primaryStage.show();
}

public static void main(String[] args) {
    launch(args);
}

addTreeView是一个从SQL DB读取数据的函数,并根据该数据添加约35 TreeItem s。向TreeItem添加treeItemRoot s是在单独的线程中完成的。 注意: treeItemRoot 在主类中声明,在此之前为null。

public TreeView addTreeView() {                                             //Our treeView is positioned on the LEFT
        treeItemRoot = new PlugTreeItem<>("Active Plugs", new ImageView(new Image(getClass().getResourceAsStream("graphics/plugicon.png"))), new Plug());          //Root of the tree, contains a dummy Plug object.
        selectedTreeItem = treeItemRoot;

        treeItemRoot.setExpanded(true);                                         //always expand it

        selectedTreeItem.getPlugItem()
                .getSIHUid().addListener(new ChangeListener<String>() {
                    @Override
                    public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue
                    ) {
                        System.err.println("changed " + oldValue + "->" + newValue);
                    }
                }
                );

        TreeView<String> treeView = new TreeView<>(treeItemRoot);               //Build the tree with our root node.

        final Task task;
        task = new Task<Void>() {
            @Override
            protected Void call() throws Exception {

                //=========== SQL STUFF BEGINS HERE ============================
                Statement sta = null;
                ResultSet result_set = null;
                Connection conn = null;

                try {
                    try {
                        System.err.println("Loading JDBC driver...");
                        Class.forName("com.mysql.jdbc.Driver");
                        System.err.println("Driver loaded!");
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException("Cannot find JDBC driver in the classpath!", e);
                    }

                    System.err.println("Connecting to database...");
                    conn = DriverManager.getConnection("[DB link here]", "[username]", "[password]");           //Username is PlugControl, pw is woof
                    System.err.println("Connected to Database!");

                    sta = conn.createStatement();
                    String sql_query = "SELECT * FROM pwnodes INNER JOIN pwcomports ON pwnodes.NetworkID = pwcomports.NetworkID WHERE pwnodes.connection = 'on' ORDER BY pwnodes.Location";

                    result_set = sta.executeQuery(sql_query);
                    System.err.println("SQL query successfuly executed!");

                    int count = 0;
                    while (result_set.next()) {
                        Plug pl = null;                                         //MARKER: We might need to do switch (result_set.getString("Server")) for SIHU1 and SIHU2.
                        count++;
                        pl = new Plug(result_set.getString("SIHUid"), result_set.getString("sensorID"), result_set.getString("Location"), result_set.getString("Appliance"), result_set.getString("Type"), result_set.getString("connection"), result_set.getString("Server"), result_set.getString("ServerIP"));
                        PlugTreeItem<String> pti = new PlugTreeItem(pl.getSIHUid().getValue() + " " + pl.getLocation() + " " + pl.getAppliance(), new ImageView(new Image(getClass().getResourceAsStream("graphics/smiley.png"))), pl); //icon does not work in children
                        treeItemRoot.getChildren().add(pti);                    //CONCURRENCY ERRORS HERE
                    }
                    System.err.println("ALERT SQL QUERY RESULTS: " + count);

                } catch (SQLException e) //linked try clause @ line 50
                {
                    throw new RuntimeException("Cannot connect the database!", e);
                } finally {   //  Time to wrap things up, by closing all open SQL procs. 
                    try {
                        if (sta != null) {
                            sta.close();
                        }
                        if (result_set != null) {
                            result_set.close();
                        }
                        if (conn != null) {
                            System.err.println("Closing the connection.");
                            conn.close();
                        }
                    } catch (SQLException e) //We might as well ignore this, but just in case.
                    {
                        throw new RuntimeException("Error while closing up statement, result set and connection!", e);
                    }
                }

                //============== SQL STUFF ENDS HERE ===========================
                System.err.println("Finished");
                return null;
            }
        };

        new Thread(task).start();                                               //Run the task!

        treeView.getSelectionModel()
                .selectedItemProperty().addListener(new ChangeListener() {
                    @Override
                    public void changed(ObservableValue observable, Object oldValue, Object newValue
                    ) {
                        selectedTreeItem = (PlugTreeItem<String>) newValue;
                        System.err.println("DEBUG: Selection plug SIHUid: " + selectedTreeItem.getPlugItem().print());           //MARKER: REMOVE
                        updateTextFields();                                             //Update TextAreas.
                        if (!"DUMMY".equals(selectedTreeItem.getPlugItem().getSIHUid().getValue())) {
                            buttonOn.setDisable(false);
                            buttonOff.setDisable(false);
                        } else {
                            buttonOn.setDisable(true);
                            buttonOff.setDisable(true);
                        }
                    }
                }
                );
        return treeView;
    }

每5-6次运行一次,我会收到一个ConcurrentModificationException,因为我认为线程任务()无法在将addTreeView返回TreeView之前设置为border_pane ,也许它开始迭代它,而它仍然有项目添加到它?

Executing C:\Users\74\Documents\NetBeansProjects\PlugControl_v0.5\dist\run1559674105\PlugControl.jar using platform C:\Program Files\Java\jdk1.7.0_25\jre/bin/java
Loading JDBC driver...
Driver loaded!
Connecting to database...
Connected to Database!
SQL query successfuly executed!
java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:819)
    at java.util.ArrayList$Itr.next(ArrayList.java:791)
    at com.sun.javafx.collections.ObservableListWrapper$ObservableListIterator.next(ObservableListWrapper.java:681)
    at javafx.scene.control.TreeItem.updateExpandedDescendentCount(TreeItem.java:788)
    at javafx.scene.control.TreeItem.getExpandedDescendentCount(TreeItem.java:777)
    at javafx.scene.control.TreeView.getExpandedDescendantCount(TreeView.java:864)
    at javafx.scene.control.TreeView.updateTreeItemCount(TreeView.java:873)
    at javafx.scene.control.TreeView.impl_getTreeItemCount(TreeView.java:533)
    at com.sun.javafx.scene.control.skin.TreeViewSkin.getItemCount(TreeViewSkin.java:207)
    at com.sun.javafx.scene.control.skin.TreeViewSkin.updateItemCount(TreeViewSkin.java:220)
    at com.sun.javafx.scene.control.skin.TreeViewSkin.handleControlPropertyChanged(TreeViewSkin.java:135)
    at com.sun.javafx.scene.control.skin.SkinBase$3.changed(SkinBase.java:282)
    at javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:107)
    at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:196)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
    at javafx.beans.property.IntegerPropertyBase.fireValueChangedEvent(IntegerPropertyBase.java:123)
    at javafx.beans.property.IntegerPropertyBase.markInvalid(IntegerPropertyBase.java:130)
    at javafx.beans.property.IntegerPropertyBase.set(IntegerPropertyBase.java:163)
    at javafx.scene.control.TreeView.setTreeItemCount(TreeView.java:515)
    at javafx.scene.control.TreeView.updateTreeItemCount(TreeView.java:876)
    at javafx.scene.control.TreeView.impl_getTreeItemCount(TreeView.java:533)
    at javafx.scene.control.TreeCell.updateItem(TreeCell.java:391)
    at javafx.scene.control.TreeCell.access$000(TreeCell.java:67)
    at javafx.scene.control.TreeCell$1.invalidated(TreeCell.java:95)
    at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:155)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
    at javafx.beans.property.ReadOnlyIntegerWrapper$ReadOnlyPropertyImpl.fireValueChangedEvent(ReadOnlyIntegerWrapper.java:195)
    at javafx.beans.property.ReadOnlyIntegerWrapper.fireValueChangedEvent(ReadOnlyIntegerWrapper.java:161)
    at javafx.beans.property.IntegerPropertyBase.markInvalid(IntegerPropertyBase.java:130)
    at javafx.beans.property.IntegerPropertyBase.set(IntegerPropertyBase.java:163)
    at javafx.scene.control.IndexedCell.updateIndex(IndexedCell.java:112)
    at com.sun.javafx.scene.control.skin.VirtualFlow.setCellIndex(VirtualFlow.java:1596)
    at com.sun.javafx.scene.control.skin.VirtualFlow.addLeadingCells(VirtualFlow.java:1049)
    at com.sun.javafx.scene.control.skin.VirtualFlow.layoutChildren(VirtualFlow.java:1005)
    at com.sun.javafx.scene.control.skin.VirtualFlow.setCellCount(VirtualFlow.java:206)
    at com.sun.javafx.scene.control.skin.TreeViewSkin.updateItemCount(TreeViewSkin.java:225)
    at com.sun.javafx.scene.control.skin.TreeViewSkin.handleControlPropertyChanged(TreeViewSkin.java:135)
    at com.sun.javafx.scene.control.skin.SkinBase$3.changed(SkinBase.java:282)
    at javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:107)
    at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:196)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
    at javafx.beans.property.IntegerPropertyBase.fireValueChangedEvent(IntegerPropertyBase.java:123)
    at javafx.beans.property.IntegerPropertyBase.markInvalid(IntegerPropertyBase.java:130)
    at javafx.beans.property.IntegerPropertyBase.set(IntegerPropertyBase.java:163)
    at javafx.scene.control.TreeView.setTreeItemCount(TreeView.java:515)
    at javafx.scene.control.TreeView.updateTreeItemCount(TreeView.java:876)
    at javafx.scene.control.TreeView.impl_getTreeItemCount(TreeView.java:533)
    at javafx.scene.control.TreeCell.updateItem(TreeCell.java:391)
    at javafx.scene.control.TreeCell.access$000(TreeCell.java:67)
    at javafx.scene.control.TreeCell$1.invalidated(TreeCell.java:95)
    at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:155)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
    at javafx.beans.property.ReadOnlyIntegerWrapper$ReadOnlyPropertyImpl.fireValueChangedEvent(ReadOnlyIntegerWrapper.java:195)
    at javafx.beans.property.ReadOnlyIntegerWrapper.fireValueChangedEvent(ReadOnlyIntegerWrapper.java:161)
    at javafx.beans.property.IntegerPropertyBase.markInvalid(IntegerPropertyBase.java:130)
    at javafx.beans.property.IntegerPropertyBase.set(IntegerPropertyBase.java:163)
    at javafx.scene.control.IndexedCell.updateIndex(IndexedCell.java:112)
    at com.sun.javafx.scene.control.skin.VirtualFlow.setCellIndex(VirtualFlow.java:1596)
    at com.sun.javafx.scene.control.skin.VirtualFlow.getCell(VirtualFlow.java:1500)
    at com.sun.javafx.scene.control.skin.VirtualFlow.getCellLength(VirtualFlow.java:1523)
    at com.sun.javafx.scene.control.skin.VirtualFlow$3.call(VirtualFlow.java:478)
    at com.sun.javafx.scene.control.skin.VirtualFlow$3.call(VirtualFlow.java:476)
    at com.sun.javafx.scene.control.skin.PositionMapper.computeViewportOffset(PositionMapper.java:143)
    at com.sun.javafx.scene.control.skin.VirtualFlow.layoutChildren(VirtualFlow.java:1001)
    at javafx.scene.Parent.layout(Parent.java:1018)
    at javafx.scene.Parent.layout(Parent.java:1028)
    at javafx.scene.Parent.layout(Parent.java:1028)
    at javafx.scene.Parent.layout(Parent.java:1028)
    at javafx.scene.Scene.layoutDirtyRoots(Scene.java:516)
    at javafx.scene.Scene.doLayoutPass(Scene.java:487)
    at javafx.scene.Scene.access$3900(Scene.java:170)
    at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2203)
    at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:363)
    at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:460)
    at com.sun.javafx.tk.quantum.QuantumToolkit$9.run(QuantumToolkit.java:329)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.access$100(WinApplication.java:29)
    at com.sun.glass.ui.win.WinApplication$3$1.run(WinApplication.java:73)
    at java.lang.Thread.run(Thread.java:724)
ALERT SQL QUERY RESULTS: 35
Closing the connection.
Finished

关于处理这个问题的任何帮助/建议,伙计们?例外并没有指向我的代码中的一个地方,到目前为止我正在努力工作。}

编辑:作为参考,PlugTreeItem只是一个TreeItem&lt;&gt;带有Plug的插件,插件是我的一类,包含一些String值。没什么特别的。 public class PlugTreeItem<T> extends TreeItem{ \* code *\}

2 个答案:

答案 0 :(得分:1)

我建议你在List<Plug>内加while loop,而不是制作单个对象并将其添加到树中,因为all operations on javafx controls必须在javafx thread上完成不在任务线程上!

在Thread主体外部创建一个列表

List<Plug> listOfPlugs = new ArrayList<Plug>();

然后,在while循环中,您可以编写

int count = 0;
while (result_set.next()) {
    Plug pl = null;                                        
    count++;
    pl = new Plug(result_set.getString("SIHUid"), 
       result_set.getString("sensorID"), result_set.getString("Location"), 
       result_set.getString("Appliance"), result_set.getString("Type"), 
       result_set.getString("connection"), result_set.getString("Server"),
       result_set.getString("ServerIP"));
    listOfPlugs.add(p1);                  
}

稍后,在启动线程后,您可以创建以下代码

new Thread(task).start();
task.setOnSucceeded(new EventHandler<WorkerStateEvent>()
{ 
   @Override 
   public void handle(WorkerStateEvent workerStateEvent) { 
      for(Plug p1 : listOfPlugs)
      {
         PlugTreeItem<String> pti = new PlugTreeItem(pl.getSIHUid().getValue() 
         + " " + pl.getLocation() + " " + pl.getAppliance(),
         new ImageView(new Image(
         getClass().getResourceAsStream("graphics/smiley.png"))), pl); 
         treeItemRoot.getChildren().add(pti);
      }
}

答案 1 :(得分:0)

我没有看到你在javafx应用程序线程上同步,这是来自另一个时需要的