从硬件出发:

1、各处理器有自己的缓存,但当多个处理器涉及的同一块主内存,处理器需要遵循一些一致性协议来操作主内存。Java Memory Model(JMM)与此类似。

2、为了处理器内部的运算单元能够最大化得到利用,处理器可能会对输入代码进行乱序运行优化。Java虚拟机的编译器也有指令重排序的功能。

Java 内存模型

分为整个虚拟机范围的主内存和各个线程的工作内存;线程只能操作自己的工作内存,不能直接操作主内存或其他线程的工作内存(线程间的变量操作只能通过主内存来)。JMM来管理从工作内存到主内存的load和save。 Java虚拟机里的线程,与linux的线程类似,后者的共享内存跟Java虚拟机的主内存概念有点类似。

JMM定义了如下内存操作:

  • lock:作用于主内存,锁定;比c线程方便的一点是,已获得锁的线程可以lock多次,只是解锁也需要unlock同样次数。注意lock/unlock用户不能直接操作。
  • unlock:作用于主内存,解锁
  • read:从主内存到工作内存
  • load:工作内存到变量副本
  • use:作用于变量,当字节码需要变量值时
  • assign:赋值
  • store:变量值回刷到工作内存
  • write:工作内存回刷到主内存

volatile

简单来说,volatile类型的变量,其读、写操作,必须直接作用到主内存(但仍要经过主内存到工作内存过程),这样带来两个特效: 1、某一线程对volatile变量的修改,可以立即反映到其他线程(因为作用于主内存,所有线程课件);但volatile变量不是线程安全的,即不能有多个写者(可以有1个写者多个读者) 2、volatile可以禁止指令重排优化(必须刷到主内存,因此需要增加内存屏障),防止出现一些低概率时序bug(例如书上提到的根据inited变量来判断若已经初始化结束则读取初始化的变量,重排可能造成最后的初始化指令在inited之后执行,而volatile变量可以保证在我之前的所有工作线程修改都刷到主内存了)

原子性、可见性、有序性

  • 原子性:可以认为所有的基本类型都是原子性的;其他情况可以使用synchronize得到原子性(lock/unlock)
  • 可见性:volatile可见性最好,普通变量可以通过synchronize(unlock时必须将修改刷回主内存)、final(初始化后所有线程可见)得到
  • 有序性:volatile/synchronize可以获得

synchronize万能,但不一定是最优解。

Java线程

现在的JVM会将Java线程对应为操作系统的轻量级进程(即线程),不再赘述。