Java强制易失性访问

Java强制易失性访问,java,multithreading,volatile,Java,Multithreading,Volatile,考虑这样一种情况 有两个线程和一个共享资源(如HashMap)。一个线程创建了HashMap,并用一些键值对对对它进行了初始化,在共享资源初始化之后,它将不再被修改 现在,第二个线程是在共享资源初始化之后创建的,并且希望使用该资源。在这一点上,我想保证第二个线程将使用正确版本的共享资源。我假设有可能第一个线程在创建第二个线程之前没有刷新对主内存的更改,因此第二个线程将把共享资源的旧值带到它的缓存中 这个分析对不对,,以及如何在初始化共享资源后手动强制刷新到Java中的主内存,在这种特殊情况下,我

考虑这样一种情况

有两个线程和一个共享资源(如HashMap)。一个线程创建了HashMap,并用一些键值对对对它进行了初始化,在共享资源初始化之后,它将不再被修改

现在,第二个线程是在共享资源初始化之后创建的,并且希望使用该资源。在这一点上,我想保证第二个线程将使用正确版本的共享资源。我假设有可能第一个线程在创建第二个线程之前没有刷新对主内存的更改,因此第二个线程将把共享资源的旧值带到它的缓存中


这个分析对不对,,以及如何在初始化共享资源后手动强制刷新到Java中的主内存,在这种特殊情况下,我不希望或不需要
volatile
synchronized

如果将HashMap声明并初始化为静态字段,它将由Java类加载器在线程安全的环境中初始化时尚。

说:

对线程的启动调用发生在已启动线程中的任何操作之前

所以,如果您的代码与您的描述匹配,那么它是安全的。

如果映射初始化发生在第二个线程开始之前,那么一切都是正确的。为了简化分析并使事情变得简单,您可以将初始映射转换为一些不可变的映射实现,并显式地将其传递给创建的线程。这样,您根本不需要使用共享变量

这个分析是否正确,以及如何在初始化共享资源后手动强制刷新Java主内存,就像在我不希望或不需要volatile或synchronized的特定情况下一样

不能不要求
volatile
synchronized
。你必须在线程之间使用某种形式的内存同步,否则就无法工作

您可以使用Andrei提到的
静态
初始值设定项(*),或
最终
,两者都意味着内存障碍。但你必须用点什么

您可能需要同步映射()或,但仍然需要使用
volatile
synchronized
final
static
来保护字段本身

Brian Geotz的C.f.,还有这个(请注意,OP把书的名字搞错了)

(*整个静态初始值设定项的事情有点复杂,你应该读Goetz先生的书,但我会尝试简单描述:
static
字段是类初始化的一部分。每个静态字段或静态初始值设定项块都是由线程编写或执行的(可能是调用
new
或第一次访问类对象的线程,也可能是不同的线程)。当第一次写入所有
静态
字段的过程完成时,JVM会插入一个内存屏障,以便按照规范的要求,系统中的所有线程都可以看到类对象及其所有静态字段

每次字段写入都不会有内存障碍,比如
volatile
。类加载试图提高效率,并且在初始化的最后只插入一个障碍。因此,您应该只使用
静态
初始值设定项来完成它们的任务:第一次填充字段,而不要尝试编写整个pro在静态初始值设定块中,它不是有效的,而且线程安全的选项实际上更有限


然而,作为类初始化一部分的内存屏障是可以使用的,这就是为什么Andrei Amarfii说,在Java中使用静态初始化器的模式用于确保对象的可见性。Brian Goetz将其称为四种“安全发布”模式之一,这一点非常重要。)

@Robert这根本不是真的。我对你的要求有点困惑。如果你想使用“synchronized”使某个线程与out同步,请使用ConcurrentHashmap。如果你想确保一个线程先工作,然后另一个线程工作,你可以设置重入锁,让第二个线程等待hashmap完成释放。或者您可以用另一个线程启动一个线程,以绝对保证第二个线程将使用正确的版本。共享对象的初始化需要在运行时执行,并且由第一个线程完成……假设“第二个线程是在真正意味着根据Java内存模型进行排序,例如,如果第一个线程调用
start()
在映射完全初始化之后。举个反例,依靠
线程。sleep
通过计时来假定顺序是不正确的。@Holger是的。请在实践中阅读Java并发性。Brian Goetz解释了它的工作原理。基本上,
静态
意味着字段由类加载器初始化,并且在在类加载结束时,必须有内存障碍,因为系统中的任何线程都可以访问类对象,JVM必须确保对它的所有初始写入都是可见的。还要阅读我链接到的关于安全发布的问题;明确提到了静态初始化。如果您正在初始化类中的
static
字段初始化器,你会得到一个内存障碍,但这是初始化器的一个属性,不是因为字段是
静态的
,因为一个字段是
静态的
,并不意味着它将在类初始化器中初始化,也不意味着它以后将永远不会被修改在任何时候使用
static
字段都是安全的,但事实并非如此。据我记忆所及,本书中的所有示例都使用了
static final
,强制正确使用,但与您的“
final
static
”不匹配。我将升级