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

为何添加synchronized的Azure Function未出现排队变慢问题?

函数实现

我编写了如下存在竞态条件的Azure Function:

public class Function {

    final RefWrapper toReturn = new RefWrapper();

    private static class RefWrapper {
        String value = "";
    }

    @FunctionName("raceConditionTest")
    public HttpResponseMessage raceConditionTest(
        @HttpTrigger(
            name = "req",
            methods = {HttpMethod.GET},
            authLevel = AuthorizationLevel.ANONYMOUS
        ),
        HttpRequestMessage<Optional<String>> request,
        final ExecutionContext context
    ) throws IOException {
        toReturn.value = request.getHeaders().getOrDefault("x-return-me", "0");
        Thread.sleep(1000 * 10);
        return request.createResponseBuilder(HttpStatus.OK).body(toReturn.value).build();
    }
}

当我并行发起10次调用,每次请求头携带不同的x-return-me值时,10个请求全部返回相同值,符合预期——确实触发了竞态条件。随后我为方法添加synchronized修饰,同时对toReturn对象加同步锁,修改后代码如下:

@FunctionName("raceConditionTest")
public synchronized HttpResponseMessage raceConditionTest( /*args*/ ) {
    synchronized (toReturn) {
        toReturn.value = request.getHeaders().getOrDefault("x-return-me", "0");
        Thread.sleep(1000 * 10);       
        return request.createResponseBuilder(HttpStatus.OK).body(toReturn.value).build();
    }
}

再次发起并行请求时,符合预期的是每个请求都正确返回对应的传入值,竞态条件被成功消除。

但出现了不符合预期的现象:所有请求几乎同时返回,并没有出现排队等待的情况。我查看了监控指标,确认运行过程中没有创建额外的函数实例。

问题咨询

请问为什么这段添加了同步锁的代码没有出现执行变慢的情况,请求为何不需要排队等待?我猜测该现象和Azure Function运行时有关,想确认该行为是否有官方保障、是否有对应文档说明,是否属于可稳定依赖的预期行为?


问题解答

核心原因是对Azure Functions Java的实例模型理解存在偏差:监控里看到的「没有额外函数实例」指的是平台没有扩容出额外的应用进程/容器实例,但同一个应用进程内部,Java worker并不保证只会创建一个Function类的对象处理所有请求,添加的进程内锁根本没有作用在跨请求共享的对象上。

现象解释

  • 第一次无锁测试触发竞态:当时10个并发请求刚好被调度到同一个Function类对象上处理,实例成员toReturn被多个工作线程共享。所有线程几乎同时进入方法,先后修改toReturn.value为自己携带的请求头值,10秒sleep结束时所有线程读到的都是最后一次写入的值,因此全部返回相同结果。
  • 第二次加锁后竞态消失但无排队:这次10个并发请求被worker分配到了多个独立的Function类对象上处理,每个类对象持有自己独立的toReturn成员。添加的两层同步锁:
    • 方法级synchronized锁的是当前Function类的对象实例
    • 同步块synchronized (toReturn)锁的是当前实例下的toReturn对象
      两个锁的锁对象都是每个请求专属的,不同请求的锁完全不相关,不会产生互斥阻塞,因此所有请求可以并行执行,总耗时就是单次请求的10秒,看起来几乎同时返回;同时每个请求操作的是自己实例的toReturn.value,不会被其他线程覆盖,竞态自然消失。

官方规则与使用建议

  • 该行为是Java worker的正常调度表现,但没有任何确定性保障:官方明确说明,Java函数类的实例生命周期完全由运行时自主管理,运行时既可能在多个调用间复用同一个类实例,也可能为每个调用创建新的类实例,分配逻辑会随并发压力、worker版本、参数配置动态调整,没有稳定可预测的规则。
  • 绝对不要依赖进程内的实例变量、实例级锁做跨调用的状态共享或者并发控制:当前观测到的现象不具备稳定性,比如低并发下所有请求被调度到同一个类实例时,同步锁就会生效,请求会排队,耗时陡增,业务逻辑会出现预期外的问题。
  • 如果需要实现跨调用的状态共享、并发互斥,必须使用独立于函数实例的外部组件,比如分布式锁、数据库、共享缓存等,不要依赖进程内的同步机制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 10:36:42