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

在Runnable中使用Collections.sort排序为何偶尔出现重复项?

解决联系人列表排序后出现重复/缺失项的问题

问题根源

  1. 并发修改同一列表实例:代码直接操作mContactListLiveData.getValue()返回的原列表,当updateSortedContactList被多次调用时,前一次的排序任务若未被成功终止(cancel(true)无法中断正在执行的排序操作),会和新任务同时修改同一个列表,引发数据错乱。
  2. 原地排序的副作用:Collections.sort是原地排序,若该列表还被其他组件持有引用,并发访问会导致数据异常。
  3. 任务取消逻辑失效:cancel(true)仅能中断处于等待状态的线程,对已在执行的排序任务无效,新旧任务的排序操作会冲突。

修复方案

1. 创建列表副本,隔离修改操作

每次排序前复制原列表,确保每个排序任务操作独立的列表实例,避免互相干扰:

List<Contact> contactList = new ArrayList<>(mContactListLiveData.getValue());

2. 改用单线程执行器,避免并发冲突

初始化线程池时使用SingleThreadExecutor,保证同一时间只有一个排序任务执行,从根源消除并发问题:

private ExecutorService mExecutorService = Executors.newSingleThreadExecutor();

此时无需手动取消任务,新任务会自动排队等待前一个任务完成。

3. 优化任务取消逻辑(若保留多线程池)

如果必须使用多线程池,将取消参数改为false,避免中断正在执行的线程,让任务自然完成,同时通过列表副本隔离修改:

mRunnableFuture.cancel(false);

修复后的完整代码

private void updateSortedContactList() {
    List<Contact> sourceList = mContactListLiveData.getValue();
    if (sourceList == null || sourceList.isEmpty()) {
        setValue(null);
        return;
    }

    if (mRunnableFuture != null) {
        mRunnableFuture.cancel(false);
    }

    Runnable runnable = () -> {
        // 创建独立副本,避免修改原列表
        List<Contact> contactList = new ArrayList<>(sourceList);
        Collections.sort(contactList, comparator);
        postValue(new Pair<>(sortMethod, contactList));
    };
    mRunnableFuture = mExecutorService.submit(runnable);
}

额外注意事项

  • 禁止直接修改LiveData返回的列表实例,因为LiveData的数据源可能被多个观察者共享,修改原实例会导致所有观察者拿到错乱数据。
  • 若联系人数据量较小,可直接在主线程执行排序,完全避免多线程并发问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 23:45:31