深入理解JMM

深入理解JMM  more understanding about jmm  java memory model(本站开始做英文SEO了)

深入理解JMM的重点

JMM具体规定要JLS的 “Thread and lock”一章中,可以说这是一章非常晦涩的一个规范,要想完全把
它理解清楚,一般的辛苦是不行的.那是要”相当的~~~”的辛苦.而要把它向别人再解释清楚,那简直就
是恶梦.

作者自知无力能全面清楚地向大家说明这一章的内容,但以作者的经验,主要从以下两个方面去理解
可以改快地抓住本质.而不至于陷入”Thread and lock”的泥潭.

一.理解主存储区和线程工作存储区.

二.理解同步的两个功能.

首先要明白的问题:
1.多个线程共有的字段应该用synchronizedvolatile来保护.
2.synchronized负责线程间的互斥.即同一时候只有一个线程可以执行synchronized中的代码.
3.volatile负责线程中的变量与主存储区同步.但不负责每个线程之间的同步.

[Main Memory]与[Working Memory]

java内存模型

Main Memory是实例所在的存储区,所有实例和实例的字段都在此区域,为所有线程所共有.
Working Memory是绺个线程独自所拥有的存储区.其中有Main Memory中部分COPY.

这两个存储区只在JVM内部与物理存储区无关.
如何理解Main Memory 与Working Memory.

设想两个棋手要通过两个终端显示器(Working Memory)对奕,而观众要通过服务器大屏幕(Main Memory )
观看他们的比赛过程.这两个棋手相当于是同步中的线程,观众相当于其它线程.

棋手是无法直接操作服务器的大屏幕的.他只能看到自己的终端显示器,只能先从服务器上将当前
结果先COPY到自己的终端上,然后在自己的终端上操作.将操作的结果记录在终端上,然后在某一时刻同步到
服务器上.他所能看到的结果就是从服务器上COPY到自己的终端上的内容,而要想把自己操作后的结果让其他
人看到必须同步到服务器上才行.至于什么时候同步,那要看终端和服务器的通信机制.

为什么要规定这么复杂的规范?因为要预留JVM的优化空间,如果规定所有的计算结果必须同步到主存储区,
那么对于方法中的计算顺序,赋值顺序等就没有优化的可能了.
引用字段:
线程在引用字段时不能直接从Main Memory中引用(JAVA1.2以前如此),如果Working Memory中没
有该字段,则会从Main Memory中将该字段COPY到Working Memory中,这个过程为read-load,完成后线程会
引用该COPY.
当同一线程再度引用该字段时,有可能重新从Main Memory中获取COPY(read-load-use),也有可能
直接引用原来的COPY(use),也就是说 read,load,use顺序可以由JVM实现系统决定.

字段赋值:
线程在字段赋值时不能直接为Main Memory中字段赋值(JAVA1.2以前如此),线程会将值指定给Working
Memory中的字段COPY(assign),完成后这个COPY会同步到主存储区(store-write),至于何时同步过去,根据JVM
实现系统决定.
有该字段,则会从Main Memory中将该字段COPY到Working Memory中,这个过程为read-load,完成后线程会
引用该COPY.
当同一线程多次重复对字段赋值时,比如:
for(int i=0;i<100;i++)
filldX = …..;
线程有可能只对Working Memory中的COPY进行赋值,只到最后一次赋值后才同步到主存储区.
所以assign,store,weite顺序可以由JVM实现系统决定.
JLS规定了6 个action,为最小操作单位,它们是原子的(atomic),JLS规定了这6个操作必须的顺序,但这个规定
是一个粗范围的,具体实现的JVM可以在这个范围内精心设计按自己的方式排序来达到最优:
[read和write] 完成将字段从Main Memory COPY到Working Memory 和从 Working Memory COPY到
Main Memory.
[use和assign] 指示线程需要从Main Memory引用字段 和 需要为Main Memory中字段赋值.
[lock和unlock] 表示线程获取锁和释放锁.

[synchronized 两个功能]

synchronized 方法 与同步块.

所有synchronized方法 最终还是同步块.
非静态的synchronized 方法相当synchronized (this){方法内容}
静态的synchronized 方法相当synchronized (XXX.clsss){方法内容}

补充:{

对一种特殊的资源——对象中的内存——Java提供了内建的机制来防止它们的冲突。由于我们通常将数据元素设为从属于private(私有)类,然后只通 过方法访问那些内存,所以只需将一个特定的方法设为synchronized(同步的),便可有效地防止冲突。在任何时刻,只可有一个线程调用特定对象的 一个synchronized方法(尽管那个线程可以调用多个对象的同步方法)。下面列出简单的synchronized方法:
synchronized   void   f()   {   /*   …   */   }
synchronized   void   g()   {   /*   …   */   }
每个对象都包含了一把锁(也叫作“监视器”),它自动成为对象的一部分(不必为此写任何特殊的代码)。调用任何synchronized方法时,对象就会 被锁定,不可再调用那个对象的其他任何synchronized方法,除非第一个方法完成了自己的工作,并解除锁定。在上面的例子中,如果为一个对象调用 f(),便不能再为同样的对象调用g(),除非f()完成并解除锁定。因此,一个特定对象的所有synchronized方法都共享着一把锁,而且这把锁 能防止多个方法对通用内存同时进行写操作(比如同时有多个线程)。
每个类也有自己的一把锁(作为类的Class对象的一部分),所以synchronized   static方法可在一个类的范围内被相互间锁定起来,防止与static数据的接触。

}

所以我们把synchronized方法合并为同步块.

当线程要进入synchronized时,Working Memory中所有没有同步到Main Memory结果会强制store-write,然后
Working Memory会被clear.那么只要再有对字段的引用当然会先read-load.

当线程要退出synchronized时,必须执行store-write,即把Working Memory中的结果同步到Working Memory中,但
退出synchronized后线程可以继续引用Working Memory中的COPY.

所以,线程在进入和退出synchronized时都把Mian Memory与Working Memory中的字段同步.
但在synchronized外和synchronized中,何时进行Mian Memory与Working Memory中的字段同步是不确定的.

大家已经非常明白这里说的”同步”是指Mian Memory与Working Memory中字段值的”一致”.即内存同步,而不是指
synchronized本身的”互斥”的意思.

加上前面所介绍的 volatile,我们知道如果要完成Mian Memory与Working Memory中字段值的”一致”,只有线程进入
或退出synchronized时,或字段是volatile时才能保证,其它的情况下,JAVA还没有方法保证.

着JVM版本的更新,以及关于JMM的JSR版本更新,JMM的修改完善会使不同时期有不同的表现,但这些修改都是为了
修复完善JLS中的规范,JAVA是向前兼容的.前面的规范本身一般不会被修改.所以掌握上面这些重点知识后,可以再
详细研究JLS的”Thread and lock”和具体的JSR.

发布评论

您的电子邮箱不会被公开。 标记为 * 的区域必须填写

*

无觅相关文章插件,快速提升流量