课程咨询: 400-996-5531 / 投诉建议: 400-111-8989
认真做教育 专心促就业
java内存模型是我们在学习java编程开发技术的时候需要重点掌握的知识要点之一,而今天我们就通过案例分析来了解一下,实现java内存模型特性的常用方法。
一、java内存模型
Java内存模型就是一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问差异的,保证了Java程序在各种平台下对内存的访问都能得到一致效果的机制及规范。
JMM与Java内存区域是两个容易混淆的概念,这两者既有差别又有联系:
区别
两者是不同的概念层次。Java内存模型是抽象的,他是用来描述一组规则,通过这个规则来控制各个变量的访问方式,围绕原子性、有序性、可见性等展开的。而Java运行时内存的划分是具体的,是JVM运行Java程序时,必要的内存划分。
联系
都存在私有数据区域和共享数据区域。一般来说,JMM中的主内存属于共享数据区域,他是包含了堆和方法区;同样,JMM中的本地内存属于私有数据区域,包含了程序计数器、本地方法栈、虚拟机栈。
在学习Java内存模型时,我们经常会提到3个特性:
可见性-Visibility
原子性-Atomicity
有序性-Ordering
Java内存模型就是围绕着在并发过程中如何处理这3个特性来建立的。
二、java内存模型特性
1、如何保证内存的可见性
那么如何保证内存的可见性,主要有三种实现方式:
volatile关键字
该关键字可以确保对一个变量的更新对其他线程马上可见。当一个变量被声明为volatile时,线程在写入变量时不会把值缓存在寄存器或者其他地方,而是会把值刷新回主内存。
sychronized关键字
一个线程在获取到监视器锁以后才能进入synchronized控制的代码块,一旦进入代码块,先,该线程对于共享变量的缓存就会失效,因此synchronized代码块中对于共享变量的读取需要从主内存中重新获取,也就能获取到新的值。
退出代码块的时候,会将该线程写缓冲区中的数据刷到主内存中,所以在synchronized代码块之前或synchronized代码块中对于共享变量的操作随着该线程退出synchronized块,会立即对其他线程可见(当然前提是线程会去主内存读取新值)。
final关键字
在对象的构造方法中设置final属性,同时在对象初始化完成前,不要将此对象的引用写入到其他线程可以访问到的地方(不要让引用在构造函数中逸出)。如果这个条件满足,当其他线程看到这个对象的时候,那个线程始终可以看到正确初始化后的对象的final属性。
2、如何保证原子性
想要保证原子性,可以尝试以下几种方式:
CAS:使用基于CAS实现的原子操作类(例如AtomicInteger)
synchronized关键字:可以使用synchronized来保证限定临界区内操作的原子性。它对应的内存间交互操作为:lock和unlock,在虚拟机实现上对应的字节码指令为monitorenter和monitorexit。
3、如何保证有序性
Java内存模型允许编译器和处理器对指令重排序以提高运行性能,并且只会对不存在数据依赖性的指令重排序。意思就是说,在Java内存模型的规定下,对编译器和处理器来说,只要不改变程序的执行结果(单线程程序和正确同步了的多线程程序),编译器和处理器怎么优化都行。在单线程下,可以保证重排序优化之后终执行的结果与程序顺序执行的结果一致(我们常说的as-if-serial语义),但是在多线程下就会存在问题。
重排序在多线程下会导致非预期的程序执行结果,想要保证可见性,可以考虑以下实现方式:
volatile
volatile产生内存屏障,禁止指令重排序。
synchronized
保证每个时刻只有一个线程进入同步代码块,相当于是让线程顺序执行同步代码。
【免责声明】:本内容转载于网络,转载目的在于传递信息。文章内容为作者个人意见,本平台对文中陈述、观点保持中立,不对所包含内容的准确性、可靠性与完整性提供形式地保证。请读者仅作参考。