图示: 每个线程都有自己独立的工作内存,线程的所有操作都是在自己的工作内存中完成的,所以每个线程的操作数都是独立的,不共享。要实现其他线程可见必须将工作内存中的变量写回主存中。 通过以下操作可以进行内存间的数据交互(最后的“()”中给出简易版解析,方便理解):
lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。(锁定变量只能被唯一线程访问)unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。(解锁,使变量可以被所有线程访问)read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中。(将数据从主存读到工作内存)load(载入):作用于工作内存的变量,将read操作得到的变量值放入工作内存的变量副本中。(将read得到的数据赋值给变量)use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。(将变量传给执行引擎)assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。(将执行引擎中的值赋值给变量)store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。(将工作内存变量的值写回主存)write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。(将store写到主存的值赋值给主存变量) PS:以上每点间的相对顺序保持一致,当然相互之间也可以插入其他操作,但不可改变它们的相对顺序。synchronized语句块:synchronized语句块保证了原子性,即某一时间段只能有一个线程对它进行访问,synchronized语句块内的操作是不可分割的。内存屏障在synchronized中的应用主要是保障它的可见性,当某线程在synchronized语句块中修改了变量时,当退出synchronized语句块时会将它写进主存,确保全局可见性。
volitate关键字:当程序对volitate变量进行了写操作时,JVM就会向处理器发送一条Lock前缀的指令,将工作内存中的变量值刷到主存中。同时每个处理器嗅探在总线上传播的数据,检查自己缓存的值是否过期,如果处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器要对这个数据进行修改操作的时候,会强制重新从系统内存里把数据读到处理器缓存里。volitate关键字只是确保可见性,不确保原子性,所以多线程修改volitate变量时会产生线程安全问题。
在上述程序中,第4行和第3行不可进行重排序,因为第4行的变量a依赖于第三行,而第2行对b赋值则可以在任何一处执行,因为对b赋值不依赖其他变量。
当进行并行排序时,如果两个线程间不存在数据依赖性,则可进行指令重排序。如果线程b的执行依赖于线程a,则不可将a、b两个线程的指令进行重排序。内存系统:程序的读写操作不一定会按照它要求处理器的顺序执行,这也是指令重排序。