一:线程数据模型与安全:
多核心(cpu)中,虚拟机是 一个主存 对应多个核心(cpu),一个cpu对应着多个内存副本,(可以理解为主存对应着堆内存,而内存副本对应着各个cpu的线程所属的内存,线程私有)。而一个线程取值时是从主存中取出复制到 副本中,而修改了值之后,会把修改后的值重新写入主存,在这个过程中,由于各个线程都有不同的值,因此如果多线程只读是没问题的,但是读写混合操作时,各个线程就会产生线程安全问题。
延伸:内存副本的数据修改后,会刷新到主存中,但是这个过程会有延迟,不是立刻就刷新数据,因此,使用volatile关键字,能强制将缓存的数据更新到主存中
二:volatile 关键字:
使用 volatile 修改变量,会强制每个要读取该变量的线程从主存中进行同步数据,保证数据是最新的。读取时会进行刷新主存操作。但是这也只能保证读取的数据是最新的,而不能使用此特性来进行数据并发安全的限制,因为变量赋值等操作在并发下不是线程安全的,因此可能多个线程已经把数据搞乱了,但是最终数据总会是一个确定的值。
原理:编译后,volatile 会生成 Lock的字节码指令,此指令会对变量所在缓存进行锁定,多个cpu核心中的副本都保存有此变量的缓存,而 volatile 变量在修改时,会有两步操作:1.直接将缓存数据同步到主存中 2.所有其他副本缓存的数据都会失效。是否失效是通过嗅探判断 主线上 的内存是否已经失效,如果失效,会重新从主线上读取最新数据,填充到缓存行中
三:synchronized 关键字:
synchronized 关键字基于两个实现,一个是同步方法,一个是同步代码块。共同点是 JVM基于进入和退出 Monitor对象来实现同步。代码块是使用 MonitorEnter 和 monitorExit指令实现。monitorenter是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处,两指令是一一对应。任何对象都与一个monitor与之关联,当且一个monitor被持有后,将处于锁定状态,线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获取对象的锁。
四:对象头
synchronized用的锁是存在java对象头里的。数组对象,使用三个字宽存储对象头,非数组类型,使用两个字宽(一字宽为4字节,即32位),对象头里的 Mark Word 里默认存储对象的HashCode、分代年龄(应该至于方法区才会对此进行计数)和锁标志位