数据库插入失败引出的多线程问题

本文记录了一个关于多线程环境下数据操作导致的奇怪问题,包括数据插入异常、排序对问题的影响以及最终的问题定位和解决方案。



昨天遇到一个奇葩问题, 一组不重复的数据在插入数据库的时候


数据 a 出现unique failed , 但是插入成功

数据 b 没有报错, 但是插入失败

并且发现for循环内都遍历不到数据b , 但是for循环外是可以打印到它的


就像鬼打墙一样搞了我6个小时查不出原因,

最后发现只要在插入前进行一次排序就不会出错, 虽然不明白原理, 姑且先这么把问题解决掉



<span style="font-size:14px;">public boolean insertClauseList(List<Clause> clauses,int stdNo){

//        Collections.sort(clauses, new Comparator<Clause>() {
//            @Override
//            public int compare(Clause lhs, Clause rhs) {
//                if (lhs.parentno != rhs.parentno) {
//                    return lhs.parentno - rhs.parentno;
//                } else {
//                    return lhs.sortby - rhs.sortby;
//                }
//            }
//        });

        SQLiteDatabase db = helper.getWritableDatabase();
        long insertResult;
        ArrayList<String> errorList = new ArrayList<>();
        for (Clause clause : clauses) {

            ContentValues values = new ContentValues();
            values.put(DBConstants.CLAUSE_NO,clause.no);
            values.put(DBConstants.CLAUSE_PARENTNO,clause.parentno);
            values.put(DBConstants.CLAUSE_SORTBY,clause.sortby);
            values.put(DBConstants.CLAUSE_CHAPTER,clause.chapter);
            values.put(DBConstants.CLAUSE_CAPTION,clause.caption);
            values.put(DBConstants.CLAUSE_GENRE,clause.genre);
            values.put(DBConstants.CLAUSE_ISCATALOG,clause.iscatalog);
            values.put(DBConstants.CLAUSE_EXPLAIN,clause.explain);
            values.put(DBConstants.CLAUSE_MANDATORY,clause.mandatory);
            values.put(DBConstants.CLAUSE_FAILING,clause.failing);
            values.put(DBConstants.CLAUSE_STDID,clause.stdid);
            values.put(DBConstants.CLAUSE_STDNO,stdNo);

            insertResult = db.insert(DBConstants.TB_CLAUSE, null, values);

            if (insertResult == -1)
                errorList.add(BaseApp.getContext().getString(R.string.ommitted,clause.no));

            if (clause.parentno == 0){
                LogUtils.w("根节点保存结果__"+insertResult);
            }

        }

        if (errorList.size() == 0){
            return true;
        }else {
            LogUtils.w("errorlist--"+errorList);
            return false;
        }
    }</span>


今天换了一台新的6.0测试机, 运行到相同的位置的时候报出CurrentModifyException, 才想起这个鬼打墙是多线程访问相同资源造成的


原因是我在网络获取数据后, 把同一个list分别传递给主线程展示和子线程进行插入数据库操作


并且, 主线程的界面在展示前对list进行了一次排序, 而此时子线程正在遍历list插入数据


结果是 子线程插入数据a完成后 主线程将数据a和数据b位置对换了, 子线程访问到该位置时读取的还是数据a的内存地址, 数据b因此没有访问到


新的机器由于性能好, 多个线程操作数据的速度更快, 才暴露出问题


由此进行如下改进


1.  避免传递源数据, 使用ArrayList(List)构造复制一个list后传递

2.  对数据进行增删改的操作放到分发之前完成, 分发后只进行遍历


问题解决



评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值