Java虚拟机高效并发之对于long和double类型变量的特殊规则以及JMM的三大特性总结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Soongp/article/details/80292796

上一篇博文,刚刚介绍了volatile的2种语义,以及JMM中对volatile变量定义的特殊规则,这些规则保证了volatile的可见性和禁止指令重排优化两种语义。以及在什么场景下可以选择JVM中最轻量级的同步同步机制来保证并发安全(满足volatile变量的2种语义)。

这篇博文主要补充一下JMM对于long和double类型变量的特殊规则,补充总结JMM的三大特性,可见性、有序性、原子性。

一、对于long和double类型变量的特殊规则

Java内存模型要求lock、unlock、read、load、use、assign、store、write这8个操作都具有原子性。但是对于64位的数据类型(long和double),在模型中特别定义了一条相对宽松的规定:允许虚拟机将没有被volatile修饰的64位数据读写操作划分为2次32位的操作来进行,即允许虚拟机实现选择可以不保证64位数据类型的read、load、store、write这4个操作的原子性。这点就是所谓的long和double的非原子协定。

如果有多个线程共享一个并未被volatile修饰的64位数据类型变量(long或double类型),并且同时对它们进行读取和修改操作,那么某些线程可能会读取到一个既非原值,也不是其他线程修改值的代表了“半个变量”的数值。

不过这种读取到“半个变量”的情况非常罕见(目前商用的Java虚拟机中不会出现),因为Java内存模型虽然允许虚拟机不把long和double类型变量的读写实现成原子操作,但允许虚拟机选择把这些操作实现为具有原子性的操作,而且还“强烈建议”虚拟机这样实现。目前各种平台下的商用虚拟机几乎都选择把64位数据的读写操作作为原子操作来对待,因为我们再编写代码时无需把long和double类型变量专门声明为volatile。

二、原子性、可见性和有序性

JMM是围绕这在并发过程中如何处理原子性、可见性和有序性这3个特性来建立的。

①原子性:由Java内存模型来直接保证的原子性变量操作包括read、load、use、assign、store、write,我们大致认为基本数据类型的访问读写是具备原子性的(无须过度在意long和double类型的非原子性协定)。

如果应用场景需要一个更大范围的原子性保证,JMM还提供了lock和unlock操作来满足这种需求,尽管虚拟机未把lock和unlock操作直接开放给用户使用,但是却提供了更高层次的字节码指令monitorenter和monitorexit来隐式的使用这种操作,这两个字节码指令反映到java代码中就是同步快——synchronized关键字,因此在synchronized块之间的操作也具备原子性。

②可见性:可见性指当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。Java内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值这种依赖主内存作为传递媒介的方式来实现可见性。无论是普通变量还是volatile变量,它们的区别是:volatile的特殊规则保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。因为,可以说volatile保证了多线程操作时变量的可见性,而普通变量不能保证这一点。

除了volatile之外,java中还有2个关键字能实现可见性,即synchronized和final(final修饰的变量,线程安全级别最高)。同步块的可见性是由“对一个变量执行unlock操作之前,必须先把此变量同步回主内存中(执行store,write操作)”这条规则获得;而final关键字的可见性是指:被final修饰的字段在构造器中一旦初始化完成,并且构造器没有把“this”的引用传递出去(this引用逃逸是一件很危险的事,其他线程有可能通过这个引用访问到“初始化了一半”的对象),那么在其他线程中就能看到final字段的值。

③有序性:JMM的有序性在讲解volatile时详细的讨论过,java程序中天然的有序性可以总结为一句话:如果在本线程内观察,所有操作都是有序的;如果在一个线程中观察另一个线程,所有操作都是无序的。前半句是指“线程内表现为串行的语义”,后半句指的是“指令重排”现象和“工作内存与主内存同步延迟”现象。

Java语义提供了volatile和synchronized两个关键字来保证线程之间操作的有序性,volatile关键字本身就包含了禁止指令重排的语义,而synchronized则是由“一个变量在同一个时刻只允许一条线程对其进行lock操作”这条规则获取的。这个规则决定了持有同一个锁的两个同步块只能串行的进入。

这篇博文完全就是概念性的总结。比较无味。“学而不思则罔”。

猜你喜欢

转载自blog.csdn.net/Soongp/article/details/80292796
今日推荐