You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaFX项目重载数据触发ConcurrentModificationException如何避免

异常根因

ConcurrentModificationException触发的核心原因是:你用来存储国家、客户等业务对象的集合(大概率是ArrayList、HashSet这类普通非线程安全集合),在被遍历执行填充TableView逻辑的同时,deleteData()方法又在对同一个集合做元素移除的结构修改操作,触发了Java集合的fail-fast校验机制,直接抛出异常。

同时你当前把JDBC数据库操作直接放在JavaFX UI线程(FXML的事件回调、initialize方法默认都运行在FX应用线程)执行,耗时的IO操作会阻塞UI渲染,容易出现界面卡顿无响应的问题。


解决方案

1. 用异步任务分离IO操作和UI操作

用JavaFX自带的Task组件把JDBC操作放到后台线程执行,所有集合修改、UI更新逻辑统一放到任务执行完成后的UI线程回调中,保证集合的修改和遍历不会同时发生:

private void reloadData() {
    // 后台任务仅处理数据库IO操作,不碰UI绑定的集合
    Task<List<Customer>> reloadTask = new Task<>() {
        @Override
        protected List<Customer> call() throws SQLException {
            // 直接从数据库查询最新客户数据,其他业务数据同理查询
            return Manager.queryAllCustomersFromDB();
        }
    };

    // 任务执行成功后切回UI线程更新数据
    reloadTask.setOnSucceeded(e -> {
        List<Customer> latestCustomerData = reloadTask.getValue();
        // 直接替换整个业务集合,不需要先删再插避免分步操作冲突
        Manager.setCustomerList(latestCustomerData);
        // 再执行TableView填充逻辑,此时集合状态稳定,没有并发操作
        customerTableView.setItems(FXCollections.observableList(latestCustomerData));
    });

    // 异常捕获逻辑
    reloadTask.setOnFailed(e -> {
        reloadTask.getException().printStackTrace();
        // 可自定义弹窗提示用户数据加载失败
    });

    // 启动后台线程执行任务
    new Thread(reloadTask).start();
}

对应的Manager层可以废弃deleteData()+loadData()的分步操作逻辑,直接查询最新数据后替换全量集合,从根源避免分步操作带来的并发冲突。

2. 兼容原有逻辑的冲突规避方案

如果一定要保留现有deleteData()+loadData()的执行逻辑,可任选以下一种方案解决冲突:

  • 把Manager层存储业务对象的集合替换为CopyOnWriteArrayList这类线程安全集合,该集合遍历时会使用快照副本,集合修改不会触发并发修改异常
  • 遍历填充TableView前,先对原集合做一次快照拷贝,遍历拷贝后的副本,原集合的修改不会影响遍历逻辑:
    // 遍历前拷贝集合快照
    List<Customer> customerSnapshot = new ArrayList<>(Manager.getCustomerList());
    for (Customer customer : customerSnapshot) {
        // 执行TableView填充逻辑
    }
    

3. 修复SQL注入风险

原有UPDATE语句采用字符串拼接的方式读取用户输入,存在严重的SQL注入风险,建议改用PreparedStatement替代普通Statement:

String updateSql = "UPDATE customers SET Customer_Name = ?, Address = ?, Postal_Code = ?, Phone = ? WHERE Customer_ID = ?";
PreparedStatement ps = DBConnection.getConnection().prepareStatement(updateSql);
ps.setString(1, nameTextField.getText());
ps.setString(2, addressTextField.getText());
ps.setString(3, postalTextField.getText());
ps.setString(4, phoneTextField.getText());
ps.setString(5, customerTextField.getText());
ps.executeUpdate();

内容的提问来源于stack exchange,提问作者Tyler Quante

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 21:12:00