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

自定义同步缓冲保存类竞态条件成因咨询

竞态条件的成因及触发场景

代码复现

public abstract class SynchronizedBufferedSaver<E> {

   private final int MAX_BUFFER_SIZE;

   private Set<E> savingBuffer;

   public SynchronizedBufferedSaver(final int max_buffer_size) {
      MAX_BUFFER_SIZE = max_buffer_size;
      savingBuffer = ConcurrentHashMap.newKeySet((int) (max_buffer_size * 1.5));
   }

   public void save(@NonNull final E entity) {
      copyToCache(entity);
      flushCache(false);
   }

   abstract protected void persist(@NonNull final Set<E> cache);

   public void flushCache(final boolean force) {
      synchronized (savingBuffer) {
         if (!savingBuffer.isEmpty() && (force || savingBuffer.size() >= MAX_BUFFER_SIZE)) {
            final Set<E> tmp = savingBuffer;
            savingBuffer = ConcurrentHashMap.newKeySet((int) (MAX_BUFFER_SIZE * 1.5));
            persist(tmp);
         }
      }
   }

   private void copyToCache(@NonNull final E entity) {
      savingBuffer.add(entity);
   }

}

核心成因

  • 锁对象动态变更,丧失互斥性:savingBuffer是非final字段,在flushCache的同步块内会被替换为新的集合实例。当线程A进入同步块并完成savingBuffer的替换后,后续线程进入同步块时,会以新的savingBuffer实例作为锁——这意味着不同线程可能持有不同的锁,完全失去了同步互斥的作用,多个线程可以同时操作不同的集合实例。
  • copyToCache无锁保护,且内存可见性缺失:copyToCache直接调用savingBuffer.add,既没有被锁同步,savingBuffer也没有用volatile修饰。这会导致两个问题:一是多个线程可以同时往同一个集合里加元素;二是线程之间对savingBuffer引用的更新不保证可见,部分线程可能一直持有旧的集合实例引用,持续往旧实例里加元素。

典型触发场景

假设MAX_BUFFER_SIZE=2,多线程并发执行save方法:

  1. 线程A、B先后执行copyToCache,往初始集合S1中添加E1、E2,此时S1大小达到阈值。
  2. 线程B率先进入flushCache的同步块,拿到S1的锁,将tmp赋值为S1,然后把savingBuffer替换为新集合S2,随后调用persist(S1)(开始处理E1、E2)。
  3. 线程C此时执行copyToCache,由于内存可见性问题,它的savingBuffer引用仍指向S1,于是往S1中添加E3。
  4. 此时如果有线程D执行flushCache(true)(强制刷入),它的savingBuffer引用可能也还指向S1,于是拿到S1的锁,发现S1不为空,就会调用persist(S1)——此时S1里包含E1、E2、E3,导致E1、E2被重复刷入持久化。

另一种场景:

  1. 线程A进入flushCache,拿到S1的锁,替换savingBuffer为S2,但还没开始执行persist(S1)。
  2. 线程E执行copyToCache,由于内存可见性问题,它的savingBuffer还是S1,往S1里添加E4。
  3. 线程A执行persist(S1),此时S1包含初始元素+E4,而如果后续线程E再次触发flushCache,它可能依然以S1为锁进入同步块,再次将S1传入persist,造成重复。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 12:57:58