多处理器硬件同步指令与相关概念解析
1. 内存读写重排序问题
在多线程编程中,单个线程对内存的读写操作可能会被重排序。这种重排序在单线程程序中是不可见的,但在多线程程序中可能会产生意外的结果。例如,一个线程填充缓冲区数据后设置指示符标记缓冲区已满,但并发线程可能在看到新数据之前就看到指示符被设置,从而读取到陈旧的值。Java 内存模型中一些违反直觉的方面会导致类似的陷阱,如第 3 章中描述的错误的双重检查锁定模式。
不同的架构对内存读写重排序的程度提供不同的保证。通常,不建议依赖这些保证,而应使用更可靠的技术来防止重排序。所有架构都允许强制写入操作按发出的顺序进行,但这需要付出代价。内存屏障指令(有时称为栅栏)可以刷新写缓冲区,确保在屏障之前发出的所有写入对发出屏障的处理器可见。原子的读 - 修改 - 写操作(如 getAndSet() )或标准并发库通常会透明地插入内存屏障。因此,只有当处理器在关键部分之外对共享变量执行读写指令时,才需要显式使用内存屏障。
内存屏障的开销很大(可能需要数百个周期甚至更多),因此应仅在必要时使用。另一方面,同步错误很难追踪,所以应适度使用内存屏障,而不是依赖复杂的特定平台对内存指令重排序限制的保证。
Java 语言本身允许在同步方法或块之外对对象字段的读写进行重排序。Java 提供了 volatile 关键字,可确保在同步块或方法之外对 volatile 对象字段的读写不会被重排序。使用该关键字可能会带来较高的开销,因此也应仅在必要时使用。理论上,可以使用 volatile 字段使双重检查锁定正确工作,但由于
超级会员免费看
订阅专栏 解锁全文

被折叠的 条评论
为什么被折叠?



