1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > JVM之Java内存模型(基于《深入理解Java虚拟机》之第12章Java内存模型与线程)(上)

JVM之Java内存模型(基于《深入理解Java虚拟机》之第12章Java内存模型与线程)(上)

时间:2021-02-24 19:03:31

相关推荐

JVM之Java内存模型(基于《深入理解Java虚拟机》之第12章Java内存模型与线程)(上)

多任务处理为什么在OS中几乎是一项必备的功能?

sadsa

sadsa①、计算机的运算能力强大了,但其运算速度与它的存储和通信子系统的速度差距太大了,不匹配,大量的时间都花费在磁盘I/O、网络通信或者数据库访问上。为了避免处理器大部分时间处于"空闲状态",所以我们要让其动起来,避免造成很大的性能浪费。

sadsa②、一个服务器要对多个客户端提供服务,这是Java语言最擅长的领域之一,我们通过参数:TPS(每秒事务处理数)来衡量服务性能的好坏。

sadsa

为什么内存和处理器之间要加入"高速缓存"?

sadsa

sadsa因为计算机的存储设备与处理器的运算速度有着几个数量级的差距,所以现代计算机系统都不得不加入一层或多层读写速度尽可能接近处理器运算速度的高速缓存来作为内存与处理器之间的缓冲。

sadsa[注]:基于高速缓存的存储交互很好地解决了处理器与内存速度之间的矛盾,但是也为计算机系统带来更高的复杂度,它引入了一个新的问题:缓存一致性(Cache Coherence)

sadsa

什么是“缓存一致性”问题呢?

sadsa

sadsa在多路处理器系统中,每个处理器都有自己的高速缓存,而它们又共享同一主内存。当多个处理器的运算任务都涉及同一块主内存区域时,将可能导致各自的缓存数据不一致,这就是"缓存一致性"问题。如图:

sadsa

什么是乱序执行呢?

sadsa

sadsa为了使处理器内部的运算单元能尽量被充分利用,处理器可能会对输入代码进行乱序执行优化,处理器会在计算之后将乱序执行的结果重组,保证该结果与顺序执行的结果是一致的,但并不保证程序中各个语句计算的先后顺序与输入代码中的顺序一致,因此如果存在一个计算任务依赖另外一个计算任务的中间结果,那么其顺序性并不能靠代码的先后顺序来保证。

sadsa[注]:Java虚拟机的即时编译器中也有指令重排序优化。

sadsa

什么是Java内存模型?

sadsa

sadsa用来屏蔽各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果。

sadsa

定义Java内存模型要注意什么?

sadsa

sadsa①、这个模型必须定义得足够严谨,才能让Java的并发内存访问操作不会产生歧义;

sadsa②、必须定义得足够宽松,使得虚拟机的实现能有足够的自由空间去利用硬件的各种特性(寄存器、高速缓存和指令集中某些特有的指令)来获取更好的执行速度。

sadsa

Java内存模型的目的是什么?

sadsa

sadsaJava内存模型的主要目的是定义程序中各种变量的访问规则,即关注在虚拟机中把变量值存储到内存和从内存中取出变量值这样的底层细节。

sadsa[注]:

sadsa①、此处的变量包括实例变量、静态变量和构建数组对象的元素,但是不包括局部变量与方法参数。因为后者是线程私有的,不会共享。

sadsa②、为了获得更好的执行效能,Java内存模型并没有限制执行引擎使用处理器的特定寄存器或缓存来和主内存进行交互,也没有限制即时编译器是否要进行调整代码执行顺序这类优化措施

sads

ssadsadsadadasdsadsasdasdasdsasadsadasdadsaJava内存模型

sadsa[注]:这里的主内存、工作内存与Java内存区域中的Java堆、栈、方法区等并不是同一个层次的对内存的划分,这两者基本上是没有任何关系的。

sadsa从功能角度可以这样划分:

sadsadasda①、主内存主要对应于Java堆中的对象实例数据部分。

sadsadasda②、工作内存则对应于虚拟机栈中的部分区域。

sadsa从更基础的层次上:

sadsadasda①、主内存直接对应于物理硬件的内存 。

sadsadasda②、而为了获取更好的运行速度,虚拟机(或者是硬件、操作系统本身的优化措施)可能会让工作内存优先存储于寄存器和高速缓存中。

sadsa

内存间的交互操作有几种?

sadsa

sadsa关于主内存与工作内存之间具体的交互协议,即一个变量如何从主内存拷贝到工作内存、如何从工作内存同步回主内存这一类的实现细节,Java内存模型中定义了8种操作(lock、unlock、read、load、assign、use、store、write)来完成。但是,为了使用方便,进行了简化,如今只用了4种:

sadsacaddsasadsadssadasadsa①、read ②、write ③、lock ④、unlock

sadsa[注]:这只是语言描述上的等价化简,Java内存模型的基础设计并未改变(仍需要8种操作) 。

sadsa

对volatile型变量的特殊规则(不具体分析,只是为了保证体系完整性,高并发中已经分析):

sadsa

sadsa关键字volatile可以说是Java虚拟机提供的最轻量级的同步机制;

sadsa

sadsa当一个变量被定义成volatile之后,它将具备两项特性:

sads234a①、对所有线程的可见性

sads2334a【注】:

sads2334saa⒈指当一条线程修改了这个变量的值,新值对于其他线程来说是可以立即得知的。而普通变量并不能做到这一点,普通变量的值在线程间传递时均需要通过主内存来完成。

sads2334saa⒉各个线程的工作内存中volatile变量也可以存在不一致的情况,但由于每次使用之前都要先刷新,执行引擎看不到不一致的情况,因此可以认为不存在一致性问题。

sads2334saa⒊volatile变量的运算在并发下一样是不安全的,因为Java里面的运算操作符并非原子操作。因为它只是保证读取的是最新的值,但是在它对这个值进行其他操作期间,可能这个值在主内存中已经改变。

sads234a②、禁止指令重排序优化

sads2334a【注】:

sads2334saa⒈有volatile修饰的变量,赋值后多执行了一个“lock addl$0x0,(%esp)”操作,这个操作的作用相当于一个内存屏障(指重排序时不能把后面的指令重排序到内存屏障之前的位置)

sads2334saa⒉只有一个处理器访问内存时,并不需要内存屏障;但如果有两个或更多处理器访问同一块内存,且其中有一个在观测另一个,就需要内存屏障来保证一致性了。

sadsa

针对long和double型变量的特殊规则:

sadsa

sadsa在Java内存模型中,要求lock、unlock、read、load、assign、use、store、write这八种(现在是4条)操作都具有原子性,但是对于64位的数据类型(lon g和 double),在模型中特别定义了一条宽松的规定:允许虚拟机将没有被volatile修饰的64位数据的读写操作划分为两次32位的操作来进行即允许虚拟机实现自行选择是否要保证64位数据类型的load、store、read和write这四个操作的原子性是。

sads2334a【注】:

sads2334saa⒈当有多个线程共享一个并未声明为volatile的long或double类型的变量,并且同时对它们进行读取和修改操作,那么某些线程可能会读取到一个既不是原值,也不是其他线程修改值的代表了“半个变量”的数值。

sads2334saa⒉在目前主流平台下商用的64位Java虚拟机中并不会出现非原子性访问行为。

sadsa

对于原子性、可见性与有序性的一些分析:

sadsa

sadsaJava内存模型是围绕着在并发过程中如何处理原子性、可见性和有序性这三个特征来建立。

sadsa

sadsa①、原子性:由Java内存模型保证了基本数据类型的访问、读写都具备原子性(long、double在32位JVM中例外,但是在64位虚拟机中也可以实现原子性)。

sads2334a【注】:

sads2334a⒈如果应用场景需要一个更大范围的原子性保证,Java内存模型还提供了lock和unlock操作来满足这种需求。

sads2334s⒉提供了更高层次的字节码指令monitorentermonitorexit来隐式地使用这两个操作。-----对应同步块(synchronized)

sadsa

sadsa②、可见性:volatile的特殊规则保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。因此我们可以说volatile保证了多线程操作时变量的可见性,而普通变量则不能保证这一点。

sads2334a【注】:还有两个关键字能实现可见性:

sads2334a⒈synchronized:对一个变量执行unlock操作之前,必须先把此变量同步回主内存中

sads2334s⒉final:被final修饰的字段在构造器中一旦被初始化完成,并且构造器没有把“this”的引用传递出去 (this逃逸分析)(this引用逃逸是一件很危险的事情,其他线程有可能通过这个引用访问到“初始化了一半”的对象),

sadsa

sadsa③、有序性:Java语言提供了volatilesynchronized两个关键字来保证线程之间操作的有序性:

sadsasdsadavolatile关键字:本身就包含了禁止指令重排序的语义

sadsasdsadasynchronized:则是由“一个变量在同一个时刻只允许一条线程对其进行lock操作”这条规则获得的,这个规则决定了持有同一个锁的两个同步块能串行地进入。

sadsa

先行发生原则:

sadsa

sadsa这些是“天然的”先行发生关系,这些先行发生关系无须任何同步器协助就已经存在,可以在编码中直接使用。

sadsa

sadsa①、程序次序规则:在一个线程内,按照控制流顺序(不是代码顺序) ,书写在前面的操作先行发生于书写在后面的操作。

sadsa②、管程锁定规则:一个unlock操作先行发生于后面对同一个锁的lock操作。

sadsa

sadsa③、volatile变量规则:对一个volatile变量的写操作先行发生于后面对这个变量的读操作

sadsa

sadsa④、线程启动规则:Thread对象的start()方法先行发生于此线程的每一个动作。

sadsa

sadsa⑤、线程终止规则:线程中的所有操作都先行发生于对此线程的终止检测

sadsa

sadsa⑥、线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生。

sadsa

sadsa⑦、对象终结规则:一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始。

sadsa

sadsa⑧、如果操作A先行发生于操作B,操作B先行发生于操作C,那就可以得出操作A先行发生于操作C的结论。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。