我今天在hibernate-jpa
的源代码内挖掘,并偶然发现了以下代码片段(您也可以在此处找到(:
private static class PersistenceProviderResolverPerClassLoader implements PersistenceProviderResolver {
//FIXME use a ConcurrentHashMap with weak entry
private final WeakHashMap<ClassLoader, PersistenceProviderResolver> resolvers =
new WeakHashMap<ClassLoader, PersistenceProviderResolver>();
private volatile short barrier = 1;
/**
* {@inheritDoc}
*/
public List<PersistenceProvider> getPersistenceProviders() {
ClassLoader cl = getContextualClassLoader();
if ( barrier == 1 ) {} //read barrier syncs state with other threads
PersistenceProviderResolver currentResolver = resolvers.get( cl );
if ( currentResolver == null ) {
currentResolver = new CachingPersistenceProviderResolver( cl );
resolvers.put( cl, currentResolver );
barrier = 1;
}
return currentResolver.getPersistenceProviders();
}
那个怪异的语句if ( barrier == 1 ) {} //read barrier syncs state with other threads
打扰了我。我花了一些时间研究volatile
关键字规范。
简单地说,在我的理解中,它可以确保相应变量上的任何READ
或WRITE
操作将直接在该值的位置直接在内存中执行。它特别防止通过持有该值副本的缓存或注册服务商访问,并且不一定要知道该值是否已更改或通过另一个核心上的并发线程修改。
因此,它会导致性能下降,因为每个访问都意味着一直进入内存,而不是使用常规(管道上的?(快捷方式。但这也确保每当线程读取变量时,它将始终是最新的。
我提供了这些详细信息,让您知道我对关键字的理解是什么。但是现在,当我重新阅读代码时,我告诉自己" 好的,我们正在通过确保一个始终为1的值始终为1(并将其设置为1(来减慢执行速度。这有什么帮助?<<"
任何人都可以解释这一点?
您了解volatile
错误。
它可以确保对相应的任何读或写操作 变量将直接在该位置的内存中执行 该值通常存储。它专门防止通过 持有该价值副本并且不是 一定要知道该值是否已更改或正在修改 另一个核心上的并发线程。
您正在谈论实现,而实现可能从JVM到JVM有所不同。
volatile
很像某种规范或规则,它可以使
写入挥发性变量,建立了与关系之前发生的 随后读取同一变量。这意味着改变 对于挥发性变量,其他线程总是可见的。什么是 更多,这也意味着当线程读取挥发性变量时, 不仅看到挥发性的最新更改,,还 导致更改的代码的效果。
和
使用简单的原子变量访问比访问更有效 这些变量通过同步代码,但需要通过 程序员避免内存一致性错误。是否额外 努力值得取决于 应用程序。
在这种情况下,volatile
不用于Gurantte barrier == 1
:
if ( barrier == 1 ) {} //read
PersistenceProviderResolver currentResolver = resolvers.get( cl );
if ( currentResolver == null ) {
currentResolver = new CachingPersistenceProviderResolver( cl );
resolvers.put( cl, currentResolver );
barrier = 1; //write
}
使用读取和写入之间的副作用是用其他线程可见的。
没有它,如果您将某些内容放在thread1中的resolvers
中,则Thread2可能不会注意到它。
使用它,如果thread2读取thread1之后读取 barrier
,则将thread2 gurant2 gurant 2查看此put
操作。
,还有许多其他同步机制,例如:
-
synchronized
关键字 -
ReentrantLock
-
AtomicInteger
- ....
通常,它们也可以构建这种情况 - 在不同线程之间的关系之前。
这是为了使对resolvers
映射完成更新到其他通过建立的线程发生在关系之前(https://www.logicbig.com/tutorials/core-java-tutorial/java-multi-threading/happens-before.html(。
在一个线程中,以下说明发生在关系之前发生
resolvers.put( cl, currentResolver );
barrier = 1;
但是,要使其他线程可见的resolvers
更改,我们需要从挥发性变量barrier
读取值,因为在关系之前写入和后续读取相同挥发性变量的建立发生(这也是传递(。因此,基本上这是总体结果:
- 更新
resolvers
- 写入挥发性
barrier
- 从挥发性
barrier
中读取步骤1中的更新可见到一个线程,该线程从barrier
读取值
挥发性变量 - 是Java中同步的轻巧形式。
声明字段volatile
将产生以下效果:
- 编译器不会重新排序操作
- 变量将不兑现在寄存器中
- 对64位数据结构的操作将作为原子一个 执行
- 它将影响其他变量的可见性同步
在实践中的Brian Goetz的并发中的引用
挥发性变量的可见性效应范围超出了值 挥发性变量本身。当线程写入挥发性时 变量和随后的线程B读取相同的变量, 在写作之前可见的所有变量的值 阅读挥发性后,B可见挥发性变量 变量。
好吧,保持1
而不将resolvers
声明为volatile WeakHashMap
的意义是什么?
此安全的出版物保证仅适用于原始字段和对象引用。出于此可见性保证的目的,实际成员是对象参考;挥发物对象引用所指的对象超出了安全出版保证的范围。因此,宣布对象引用的波动不足以确保对参考成员的更改已发布到其他线程。线程可能无法观察到从另一个线程到此类对象引用的成员字段的最新写入。
此外,当指南可变并且缺乏线程安全性时,其他线程可能会在不一致的状态下看到部分构造的对象或对象。
Map
对象的实例是由于其put()
方法而被突变的。
与get()
和put()
的交错调用可能会导致从MAP对象中检索内部不一致的值,因为put()
修改了其状态。声明对象参考volatile
不足以消除此数据竞赛。
由于volatile
变量建立了happens-before
关系,当一个线程具有更新时,它可以告知其他人访问barrier
。
从记忆可见性的角度来看,写一个动荡的 变量就像退出同步块并读取挥发性 变量就像输入同步块。