Hibernate源码中volatile barrier如何实现线程间状态同步?
嘿,这个问题问得挺有意思的!我来给你拆解一下这段Hibernate代码里的volatile barrier到底是怎么干活的。
首先得回忆一下Java里volatile关键字的核心作用:它主要管两件事——内存可见性和禁止指令重排序。当一个变量被标记为volatile时,对它的写操作会立刻把值刷到主内存,而读操作会直接从主内存取,不会用线程本地的工作内存缓存;同时,编译器和CPU不能对这个变量相关的指令乱序执行。
回到你贴的这段代码:PersistenceProviderResolverPerClassLoader用了一个WeakHashMap<ClassLoader, PersistenceProviderResolver>来存储类加载器对应的解析器,但WeakHashMap本身不是线程安全的,而且代码里还有个FIXME注释说要换成带弱引用的ConcurrentHashMap,说明当时是用了一个不太线程安全的容器,然后靠这个volatile的barrier来补线程安全的短板。
那这个barrier具体怎么实现同步的?其实它是利用了volatile变量读写时触发的内存屏障来强制线程间的状态同步:
当某个线程修改了
resolvers(比如添加新的解析器或者移除旧的),它会紧接着更新barrier的值(通常是翻转1和0,比如barrier = (short) (barrier ^ 1))。因为barrier是volatile的,这个写操作会触发两个内存屏障:- StoreStore屏障:确保在写
barrier之前,所有对resolvers的修改都已经刷到主内存,不会被缓存在线程本地。 - StoreLoad屏障:防止后续的读操作被重排序到写
barrier之前,保证状态更新的顺序性。
- StoreStore屏障:确保在写
当其他线程要访问
resolvers的时候,会先读取barrier的值。这个读操作同样会触发两个内存屏障:- LoadLoad屏障:确保后续对
resolvers的读操作是从主内存读取,而不是用线程本地的缓存值。 - LoadStore屏障:防止后续的写操作被重排序到读
barrier之前,避免出现指令乱序导致的状态不一致。
- LoadLoad屏障:确保后续对
简单来说,这个barrier就像一个“同步信号”:修改容器的线程写完容器后,更新这个信号告诉其他线程“我改完了,你们要读最新的”;而读容器的线程先看这个信号,强制自己去主内存拿最新的容器状态,从而实现了多线程之间的状态同步。
不过要注意,这种方式只能保证可见性和一定的顺序性,不能完全替代ConcurrentHashMap的线程安全特性——比如如果多个线程同时修改resolvers,还是可能出现竞态条件(比如两个线程同时put导致的覆盖),这也是为什么代码里有FIXME要换成ConcurrentHashMap的原因。
内容的提问来源于stack exchange,提问作者Aldian

