JavaFX:ConcurrentModificationException在单独的线程中在TreeView中添加TreeItem对象时
我有以下代码JavaFX:ConcurrentModificationException在单独的线程中在TreeView中添加TreeItem对象时,treeview,javafx,concurrentmodification,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 stuf
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);
}
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);
}
}
addTreeView
是一个从SQL数据库中读取数据并基于该数据添加约35个TreeItem
s的函数。将TreeItem
s添加到treeItemRoot
是在单独的线程中完成的注意: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;
}
伙计们,在处理这个问题上有什么帮助/建议吗?这个异常并没有将我指向代码中的某个位置,到目前为止,我正在研究预感
编辑:作为参考,PlugTreeItem
只是一个treeiItem,它还带有一个Plug
,Plug是我的一个类,它包含一些字符串值。没什么特别的。
公共类PlugTreeItem扩展了TreeItem{\*code*\}
我看不到您在javafx应用程序线程上同步,这是来自另一个线程时所必需的我建议您在while循环
中创建一个列表
,而不是创建单个对象并将其添加到树中,因为javafx控件上的所有操作都必须在javafx线程上完成,而不是在任务线程上
在线程体外部创建一个列表
List<Plug> listOfPlugs = new ArrayList<Plug>();
稍后,在启动线程后,可以生成以下代码
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);
}
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);
}
}
新线程(任务).start();
task.setOnSucceeded(新的EventHandler()
{
@凌驾
公共无效句柄(WorkerStateEvent WorkerStateEvent){
用于(插头p1:插头列表)
{
PlugTreeItem pti=新的PlugTreeItem(pl.getSIHUid().getValue())
+“”+pl.getLocation()+“”+pl.getAppliance(),
新图像视图(新图像(
getClass().getResourceAsStream(“graphics/smiley.png”)),pl);
treeItemRoot.getChildren().add(pti);
}
}
whatsplug
和PlugTreeItem
?@ItachiUchiha查看我的更新问题。感谢您的评论:)您的答案虽然正确,但有一个重大缺陷。由于线程使用SQL查询,因此可以在for()之后轻松完成下面的循环已开始并完成将子循环放入treeItemRoot
。解决方案是将for循环放入task.setOnSucceeded(new EventHandler(){@Override public void handle(WorkerStateEvent WorkerStateEvent){\*for loop here*\})
确保它只在任务完成后运行。我从另一个答案中找到了这一点,并结合您的答案构建了我的解决方案:是的,非常正确!我匆忙错过了这一点。我将编辑并添加它以供将来参考!!!既然您编辑了它,并且答案现在是正确的,我可以继续接受它作为答案。