Java线程同步synchronized的理解
jvm中(留神:马上讲到的这两个存储区只在jvm内部与物理存储区无关)存在一个主内存(main memory),java中所有的变量存储在主内存中,所有实例和实例的字段都在此区域,对于所有的线程是共享的(相当于黑板,其他人都可以看到的)。每个线程都有自己的工作内存(working memory),工作内存中保存的是主存中变量的拷贝,(相当于自己笔记本,只能自己看到),工作内存由缓存和堆栈组成,其中缓存保存的是主存中的变量的copy,堆栈保存的是线程局部变量。线程对所有变量的操作都是在工作内存中进行的,线程之间无法直接互相访问工作内存,变量的值得变化的传递需要主存来完成。在jmm中通过并发线程修改的变量值,必须通过线程变量同步到主存后,其他线程才能访问到。
看看这个图是不是更形象
好啦,下面来看线程对某个变量的操作步骤:
1.从主内存中复制数据到工作内存
2.执行代码,对数据进行各种操作和计算
3.把操作后的变量值重新写回主内存中
现在举个例子,设想两个棋手要通过两个终端显示器(working memory)对奕,而观众要通过服务器大屏幕(main memory )观看他们的比赛过程。这两个棋手相当于是同步中的线程,观众相当于其它线程。棋手是无法直接操作服务器的大屏幕的,他只能看到自己的终端显示器,只能先从服务器上将当前结果先复制到自己的终端上(步骤1),然后在自己的终端上操作(步骤2),将操作的结果记录在终端上,然后在某一时刻同步到服务器上(步骤3)。他所能看到的结果就是从服务器上复制到自己的终端上的内容,而要想把自己操作后的结果让其他人看到必须同步到服务器上才行。至于什么时候同步,那要看终端和服务器的通信机制。
回到这三个步骤,这个顺序是我们希望的,但是,jvm并不保证第1步和第3步会严格按照上述次序立即执行。因为根据java语言规范的规定,线程的工作内存和主存间的数据交换是松耦合的,什么时候需要刷新工作内存或者什么时候更新主存的内容,可以由具体的虚拟机实现自行决定。由于jvm可以对特征代码进行调优,也就改变了某些运行步骤的次序的颠倒,那么每次线程调用变量时是直接取自己的工作存储器中的值还是先从主存储器复制再取是没有保证的,任何一种情况都可能发生。同样的,线程改变变量的值之后,是否马上写回到主存储器上也是不可保证的,也许马上写,也许过一段时间再写。那么,在多线程的应用场景下就会出现问题了,多个线程同时访问同一个代码块,很有可能某个线程已经改变了某变量的值,当然现在的改变仅仅是局限于工作内存中的改变,此时jvm并不能保证将改变后的值立马写到主内存中去,也就意味着有可能其他线程不能立马得到改变后的值,依然在旧的变量上进行各种操作和运算,最终导致不可预料的结果。
这可如何是好呢?还好有synchronized和volatile:
1.多个线程共有的字段应该用synchronized或volatile来保护.
2.synchronized负责线程间的互斥.即同一时候只有一个线程可以执行synchronized中的代码.
synchronized还有另外一个方面的作用:在线程进入synchronized块之前,会把工作存内存中的所有内容映射到主内存上,然后把工作内存清空再从主存储器上拷贝最新的值。而在线程退出synchronized块时,同样会把工作内存中的值映射到主内存,不过此时并不会清空工作内存。这样一来就可以强制其按照上面的顺序运行,以保证线程在执行完代码块后,工作内存中的值和主内存中的值是一致的,保证了数据的一致性!
3.volatile负责线程中的变量与主存储区同步.但不负责每个线程之间的同步.
volatile的含义是:线程在试图读取一个volatile变量时,会从主内存区中读取最新的值。现在很清楚了吧。
转自