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

关于并行修改Java Protobuf及其扩展字段的安全性咨询(版本>3.17)

关于并行修改Java Protobuf及其扩展字段的安全性咨询(版本>3.17)

哥们,直接给你划重点:这种并行修改同一个Protobuf Builder的操作绝对不安全,哪怕你改的是完全不相关的普通字段或者扩展字段,Protobuf的Builder从设计之初就没考虑线程安全,3.17+版本也没有改变这个核心设计。

为啥这么说?你得知道Protobuf的Builder内部藏着不少共享状态——比如记录字段是否被修改的标记位、存储扩展字段的哈希表,这些结构的操作全都是非原子的,也没有加同步锁。举个实际的例子:当你调用setFirstLeaf的时候,Builder会更新内部的字段状态标记,这个过程如果同时有另一个线程在改secondLeaf或者扩展字段,很可能会导致标记位错乱,最后build出来的消息要么字段值被莫名覆盖、要么直接抛出ConcurrentModificationException,甚至出现一些说不清楚的半初始化状态,排查起来贼费劲。

看你贴的代码,两个异步任务直接操作同一个topBuilder:

CompletableFuture.supplyAsync(() -> buildFirstLeaf(topBuilder));
CompletableFuture.supplyAsync(() -> buildSecondLeaf(topBuilder));

这种写法风险拉满,哪怕buildFirstLeaf只碰firstLeaf,buildSecondLeaf只碰secondLeaf,也完全逃不过并发竞争的坑。

给你两个靠谱的解决方案:

  • 最推荐的方式:线程隔离处理子消息
    让每个异步任务单独生成自己负责的子消息实例,最后在主线程里统一把这些实例设置到主Builder中。这样每个任务完全不共享Builder,彻底规避并发问题,还能保留并行处理的优势。代码大概是这样:

    // 每个任务返回对应的子消息,不碰主Builder
    CompletableFuture<FirstLeaf> firstFuture = CompletableFuture.supplyAsync(this::buildFirstLeaf);
    CompletableFuture<SecondLeaf> secondFuture = CompletableFuture.supplyAsync(this::buildSecondLeaf);
    
    // 主线程等待所有任务完成,然后组装
    FirstLeaf firstLeaf = firstFuture.join();
    SecondLeaf secondLeaf = secondFuture.join();
    
    TopMessage finalMsg = TopMessage.newBuilder()
        .setFirstLeaf(firstLeaf)
        .setSecondLeaf(secondLeaf)
        // 要是有扩展字段,也在主线程统一设置
        .build();
    
  • 迫不得已的备选:手动加同步锁
    如果你因为某些特殊原因必须共享Builder(真心不推荐),那得自己给每个操作Builder的代码块加同步控制,比如用synchronized把对Builder的操作包起来:

    CompletableFuture.supplyAsync(() -> {
        synchronized(topBuilder) {
            buildFirstLeaf(topBuilder);
        }
        return null;
    });
    CompletableFuture.supplyAsync(() -> {
        synchronized(topBuilder) {
            buildSecondLeaf(topBuilder);
        }
        return null;
    });
    

    但要注意,加了同步之后,两个任务其实变成串行执行了,并行的意义基本就没了,所以除非万不得已,别选这个。

另外再提一句扩展字段的情况:哪怕你并行修改的是完全不同的扩展字段,Builder内部存储扩展的哈希表本身是非线程安全的,并发修改一样会出问题,比如丢数据、抛异常,所以本质上和普通字段的问题是一样的。

总的来说,Protobuf Builder就是给单线程用的,并发操作绝对要不得,老老实实用线程隔离的方式组装消息才是正路。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 11:13:06