requires用于声明模块单向静态依赖,可强制阻断循环依赖;它不解决变量死锁等运行时并发问题,后者需用synchronized、Lock等机制处理。
requires 声明本身不直接解决“变量死锁”,它也不是为控制运行时状态或线程同步而设计的。你提到的“变量死锁”属于并发编程范畴(如多线程竞争共享变量导致的循环等待),而 requires 是 java 平台模块系统(jpms)中用于编译期和启动期声明静态依赖关系的关键字,作用于模块层级,与运行时变量、锁、线程无关。
所以首先要厘清:
✅ requires 可以强制执行模块间的单向、显式、静态依赖原则;
❌ 它不能规避或解决“变量死锁”——那是 synchronized、ReentrantLock、volatile 或并发工具类(如 AtomicInteger、StampedLock)需要处理的问题。
下面聚焦在 如何用 requires 实战落实单向依赖原则,并说明常见误用与规避路径:
用 requires 确保模块依赖方向不可逆
单向依赖指:A → B 是允许的,但 B → A 必须被阻止(否则形成循环依赖,破坏模块内聚)。JPMS 在编译和启动阶段会主动检测并报错。
-
模块 A 的
module-info.java:module com.example.service { requires com.example.model; // ✅ 允许:service 依赖 model exports com.example.service.api; } -
模块 B(即
com.example.model)的module-info.java:module com.example.model { // ❌ 不应写:requires com.example.service // 否则编译时 javac 会报错:circular dependency detected exports com.example.model.data; }
只要任一模块反向 requires,javac 编译或 java --module-path 启动时就会失败,从而从机制上杜绝双向/循环依赖。
避免隐式传递依赖破坏单向性
requires transitive 虽便利,但容易悄悄引入“间接反向可见性”。例如:
-
com.example.core声明:module com.example.core { requires transitive com.example.logging; // 日志对下游透明 } 若
com.example.logging内部又requires com.example.core(哪怕只是测试代码或错误配置),就构成循环。
✅ 正确做法:
-
requires transitive仅用于稳定、无业务逻辑的基础设施模块(如日志、JSON 序列化); - 所有
transitive依赖自身必须是无上游业务依赖的纯工具模块; - 使用
jdeps --multi-release 25 --check <module>定期验证模块图是否含环。
单向依赖落地检查清单
- 每个
requires行只出现在高层模块依赖低层模块的位置(如 service → model,web → service); - 禁止在 domain/model 层模块中
requires任何 application/service 层模块; - 所有跨模块调用必须通过
exports显式开放的包进行,未导出包无法访问(连反射都受限); - 构建时启用
--validate-modules参数,让 JVM 启动时校验依赖图完整性。
关于“变量死锁”的澄清与建议
如果你实际遇到的是模块初始化顺序引发的静态字段相互等待(例如 Class A 的 static 块依赖 Class B 的 static 字段,而 B 又依赖 A),这属于类加载死锁,不是变量级死锁,但同样危险:
-
✅ 规避方式:
- 避免在
static块中触发其他模块类的主动初始化; - 将模块间协作延迟到实例方法,而非静态入口;
- 使用
ServiceLoader或 DI 容器解耦初始化时机。
- 避免在
❌ 不要指望
requires控制这个——它不介入类加载顺序,只约束模块可见性。
不复杂但容易忽略。

















