同步块仅原子读取/更新共享状态并“拍下”快照值,所有CompletableFuture链式操作必须在锁外基于快照值执行,严禁在块内启动异步、调用get/join或访问外部资源。

不能在 synchronized 块内部直接启动并编排 CompletableFuture 链,更不能在持有锁时注册回调或调用 get() —— 这样既没实现真正异步,又拖长了临界区,还可能引发数据不一致。
同步块只做“快照”,不碰异步逻辑
同步块的唯一任务是:原子读取/更新共享状态,并把后续异步所需的关键参数(如 ID、新值、时间戳、版本号)安全“拍下来”。这些值一旦提取完成,就应立刻退出同步块。
- 避免在块内调用
supplyAsync或thenApply等方法 - 禁止在块内调用
future.get()或join(),否则线程阻塞,锁无法释放 - 不要把数据库连接、HTTP 客户端等外部资源带进同步块
异步任务必须基于“已确定输入”独立执行
所有 CompletableFuture 的链式操作(thenApply、thenCompose、exceptionally)都应在同步块外启动,且只依赖前面捕获的快照值,不访问任何未加保护的共享变量。
Java开发手册规约集合,基于阿里巴巴Java开发手册(嵩山版)。 涵盖7大维度:编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构、设计规约。 当用户需要:(1) 编写或审查Java代码 (2) 检查命名/代码规范 (3) 处理异常和日志 (4) 编写单元测试 (5) 安全编码 (6) 数据库设...
- 例如:同步块里拿到
orderId和oldStatus,异步任务才去查库、发消息、更新日志 - 若异步步骤需再次修改共享状态(如更新缓存计数器),应另起短同步块,而不是复用旧锁
- 回调中若需写入共享对象,必须重新加锁(但要确保锁粒度小、无嵌套、无死锁风险)
典型正确结构:三步分离
以“更新订单状态 + 异步通知”为例:
立即学习“Java免费学习笔记(深入)”;
-
第一步(同步):
synchronized(orderLock) { status = updateInMemory(orderId, NEW_STATUS); timestamp = System.nanoTime(); } -
第二步(释放锁后立即):
return CompletableFuture.supplyAsync(() -> callExternalService(orderId, status, timestamp)) -
第三步(纯异步编排):
.thenAccept(result -> logSuccess(orderId, result)) .exceptionally(err -> logFailure(orderId, err))
常见误用与后果
以下写法看似简洁,实则危险:
-
synchronized(lock) { return CompletableFuture.runAsync(() -> doDBWrite()); }→ 锁未释放就返回 future,但doDBWrite仍可能读到脏数据(因它没被锁保护) -
synchronized(lock) { future.thenRun(() -> updateCache()); }→ 回调在锁外执行,updateCache若改共享变量,就失去线程安全 -
synchronized(lock) { return future.thenApply(...).join(); }→ 彻底退化为同步,还白白占着锁

















