变量clkID和clockTime存储于主内存(main memory)内。但Java语言允许线程保存这些变量的私有专用副本(private working copy),这样可以便我们的两个线程更高效地运行。当线程读写这些变量时,它们操作的是private working copy,而非main memory中的正本。private working copy只在特定的同步点(synchronization point)才与main memory的正本进行一致化动作。
对于synchronized锁包围的“代码所访问到的field”在进入,或者离开这块区域时,会强制private working copy—main memory一致化数据。
volatile类型的field,代码可以并发执行,并且保证private working copy和main memory是一致的。这种一致化动作是在每次访问这个变量的时候发生
记住,一旦变量被声明为volatile,在每次访问它们时,它们就与主内存进行一致化。但如果使用synchronized,只有在取得lock和释放lock时,才会对变量和主内存进行一致化
下面是的SerialNumberGenerator.nextSerialNumber()函数的字节码
Java递增操作不是atomic的,其涉及到一个读操作(getstatic)和一个写操作(putstatic),所以,即便是在这么简单的操作中,也为多线程问题留下来了空间。正如你所看到的,volatile在这里实际上不是什么问题,正在的问题在于nextSerialNumber()在没有synchronized的情况下对共享可变值进行了访问
如果将一个field定义为volatile,那么它就会告诉编译器不要执行任何移除读取和写入操作的优化,这么操作的目的是用线程的private working copy来维护对main memory的精确同步(1、practical java说“会在访问这个变量时来进行private working copy和main memory的一致化动作” 2、TIJ说“实际上,读取和写入操作都是直接针对内存的”)。
但是volatile并不能对递增不是atomic操作这一事实产生影响
synchronized有两个作用
1、为了互斥
2、为了通信(其实就是使得private working copy和main memory进行一致化动作)
而为了互斥的例子,我们见过很多,下面我们看个为了通信的例子
这里,每一个被同步的方法中的动作都是atomic的,使用同步的唯一目的是:为了通信而不是为了互斥访问