JavaFX:ConcurrentModificationException同时在单独的线程中在TreeView中添加TreeItem对象 [英] JavaFX: ConcurrentModificationException while adding TreeItem objects in TreeView, in a seperate thread

查看:148
本文介绍了JavaFX:ConcurrentModificationException同时在单独的线程中在TreeView中添加TreeItem对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码

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 的函数。将 TreeItem 添加到 treeItemRoot 是在一个单独的线程中完成的。 注意: treeItemRoot 在主类中声明,在此之前为空。

With addTreeView being a function that reads data off an SQL DB and adds around ~35 TreeItems, based on that data. The addition of TreeItems to treeItemRoot is done in a seperate thread. NOTE: treeItemRoot is declared in the main class, and is null before here.

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 因为我认为Thread task()无法在之前完成addTreeView 的返回 TreeView 被添加到border_pane,也许它开始迭代它,但它仍然添加了项目?

Once every 5-6 runs I receive a ConcurrentModificationException because I think the Thread task() doesn't manage to finish before addTreeView's returned TreeView is added to the border_pane, and maybe it begins iterating through it while it's still having items added to it?

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

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

Any help/advice on dealing with the issue, guys? The exception doesn't point me towards a place in my code, and so far I'm working on hunches.}

编辑:供参考, PlugTreeItem 只是一个TreeItem<>,它带有一个 Plug ,它是一个包含一些String值的我的类。没什么特别的。
public class PlugTreeItem< T>扩展TreeItem {\ *代码* \}

For reference, PlugTreeItem is just a TreeItem<> that also carries a Plug with it, Plug being a class of mine that holds a few String values. Nothing special. public class PlugTreeItem<T> extends TreeItem{ \* code *\}

推荐答案

我建议你做一个列出< Plug> 在你的 while循环中,而不是制作单个对象并将其添加到树中,因为 javafx控件上的所有操作必须在 javafx线程上完成,而不是在任务线程上完成!

I would suggest you to make a List<Plug> inside your while loop rather than making individual object and adding it to the tree, because all operations on javafx controls must be done on javafx thread and not on task thread !

在Thread主体外创建一个列表

Create a list outside the Thread body

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

然后,在while循环中,你可以写

Then, in the while loop, you can write

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

稍后,在启动线程后,您可以生成以下代码

Later, after you start the thread you can make the following code

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

这篇关于JavaFX:ConcurrentModificationException同时在单独的线程中在TreeView中添加TreeItem对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆