标准答案
- 没有同步关系时,一个线程写入的值不保证及时被另一个线程看到。
- volatile 能提供特定变量的可见性和一定有序性,但不保证复合操作原子性。
- 锁的解锁与后续对同一锁的加锁建立 happens-before,线程启动和 join 也有明确规则。
- 并发设计应优先减少共享可变状态,并使用语言提供的同步原语建立可证明的关系。
题目解析
happens-before 描述如果一个动作先于另一个动作成立,后者必须能看到前者的相关结果并遵守相应排序。它是 Java 内存模型的推理关系,不是 CPU 指令时间线的直译。
volatile 写读、锁释放获取、线程 start 和 join 等都能建立特定同步关系;没有同步边界时,即使代码按源码顺序执行,也不能把可见性当作保证。
复合操作仍要用锁、原子类或更高层状态机保护。排查并发 bug 时要画出发布、读取和同步边界,而不是依靠 sleep、日志顺序或本地机器偶然表现。
常见误区
- 误区:把 volatile 当作 i++ 的原子保证。改正:volatile 主要提供可见性和排序,复合递增需要原子类或锁。
- 误区:认为 sleep 能建立内存可见性。改正:使用锁、volatile、Future、并发容器或其他明确的同步关系。
- 误区:只给写入加同步,读取走另一条无同步路径。改正:读写双方必须遵守同一 happens-before 边界。