硬件的效率与一致性
在计算机中,内存的读写与处理器的计算速度有几个级的差距.这样会严重影响到TPS(Transations Per Second).
所以会为每个处理器配一个高速缓存以缓和处理器的速度.而在计算机中,多个处理器共享一个内存,这个时候数据的读写操作将不会安全
什么是内存模型
名词解释:JMM(Java Memory Model),即Java内存模型
如果想要深入了解Java并发编程,必须先学习好JMM,JMM定义了jvm虚拟机在计算机内存中的工作方式
工作内存与主内存
java内存模型规定了所有变量都必须存储在主内存中.
线程对变量的所有操作(赋值与读取)都必须在工作内存中进行,而不能直接读写主内存的变量
线程间也无法直接访问对象工作内存中的变量,而是通过主内存来进行交互(线程间变量值的传递需要主主内存来完成)
内存间相互交互
工作内存与主内存之间有规定的协议.即一个变量如何从主内存拷贝到工作内存中,又如何从工作内存同步变量到主内存中.以下有8个操作指令:
1.lock(锁定)作用于主内存的变量中,锁定并让某线程独享
2.unlock(解锁)作用于主内存的变量中,解锁变量以让其他线程访问
3.read(读取)作用于主内存变量中,以便工作内存下一步的load操作
4.load(载入)作用于工作内存中,载入从主内存中读取的变量
5.use(使用)作用于工作内存中.他把工作内存中的值传递给执行引擎,每当虚拟机需要使用到变量值的时执行这个操作
6.assign(赋值)作用于工作内存中,他把从执行引擎中返回的最新值赋值给变量,每当虚拟机遇到一个给变量赋值的字节指令码时执行这个操作
7.store(存储)作用于工作内存中,它把工作内存中的值传递到主内存中,以便下一步的write操作
8..write(写入)作用于主内存中,它把从store操作获取的值更新到内存中
上述的8中基本操作必须满足以下规则:
1.不允许read、load和store、write单独出现
2.不允许线程对其最近的assign操作,即工作内存中的变量改变后必须同步到主内存中
3.不允许一个线程无原因的同步变量到主内存中
4.一个新的变量只能从主内存中产生.工作内存中不允许使用未被初始化(load、assign)的变量
5.变量最多只能同时被一个线程执行load操作,且可以操作任意次,相对的需要执行一样多次的unlock操作.
6.lock操作时会清除工作内存中的变量的值.当需要读取时再从主内存中载入
7.如果一个变量没有进行lock操作那就不允许执行unlock操作,也不能去unlock其他线程锁定的变量
8.在执行unlock操作前变量必须先同步到主内存中
Volatile变量的特殊性
关键字volatile可以说是Java虚拟机最轻量级的同步机制
一个变量被定义为volatile,它将具备两种特性:
1.保证变量对所有线程的“可见性”变量的改变将会同步更新到其他的拥用该变量的线程中,而普通的变量不能做到这里点,普通变量的值在线程间传递均需要通过主内存来完成,需要注意的是:volatile只能保证变量的可见性,在多线程的环境中也并非是绝对安全的
2.使用volatile变量表示禁止指令重排序优化
线程安全指标:可见性、原子性、有序性
Java内存模型是围绕着可见性、原子性、有序性这三个特征建立的
1.原子性(Atomicity):
由Java内存模型来保证原子性的操作有read、load、assign、use、store、write, 大致可以认为基本数据类型的访问读写是具备原子性。如果需要一个更大的原子性保证,还有lock和unlock,在虚拟机中没有直接把这两操作给用户使用,而是隐式的在字节 码添加指令monitorenter和monitorenter进行操作.这两个字节码对象jdk代码中就是同步块-synchronize关键字
2.可见性(Visibility):
可见性是指当线程改变了共享的变量,其他线程能够马上知道此次的修改
2.1:volatile就验证了这一点
2.2:synchronize也是可见性的,因为在其unlock操作之前,必须把变量值同步到主内存中
2.3 final也是有可见行.因为被final修饰的字段一旦被构造器初始化完成,并且构造器没把this传递出去,那在其他线程中就能看见final字段的值(WTF?不懂)
3.有序性(Ordering):
编译器或者运行时环境为了优化程序性能,会对代码进行重排序.而volatile关键字本身就禁止指令重排序,synchronize则是由“一个变量在同一时刻只能有一个线程进行lock操作”获得.持有同一个锁的两个同步块只能串行的进行(why?)
先行发生happends-before原则
如果java内存模型中只能靠volatile和synchronize关键字来有序性的执行,那么有一些操作起来就会非常频繁,所以Java语言中有一种现行发生happens-before的原则,如下
程序次序规则:在一个线程内,按照控制流顺序,控制流前面的操作先行发生于控制流后面的操作,说“控制流”是因为还要考虑到分支、循环结构
管程锁定规则:unlock操作必须先行发生于同一个锁的lock操作
volatile规则:变量的写操作必须先于读操作
线程启动规则:thread的start()方法先于任何方法
线程终止规则:线程中的所有方法都先行与对此线程终止检测
线程中断规则:对线程的interrupt()方法调用先行发生于被中断线程的代码检测到中断事件的发生
对象终结规则:对象的初始化完成必须先发生于finalize()方法的开始
传递新:A操作先于B操作,B操作先于C操作,则A操作一定先于C操作