近几年计算性能经过重排序实现了很大的提高,并且处理器也愈来愈朝着多核处理器发展以实现硬件的并行性。随着处理器的不断强大,编译器也在不断的改进:经过指令重排序来实现优化执行,使用成熟的全局寄存器分配算法
算法。这些都使得线程在内存内的操做更趋于复杂,若是没有正确的同步机制下,内存间的操做呈现乱序执行,从而不能保证计算结果的正确性。缓存
编译器重排序:使得编译后的指令顺序能够和源代码的顺序不同安全
处理器重排序:在编译器重排序的基础上再进行指令的优化重排序多线程
处理器的并行性:多线程内线程之间的操做执行顺序不一样,要实现线程间的数据共享在没有同步的机制下易产生不安全的数据共享。架构
处理器的多级缓存:若是缓存提交至主内存的顺序没有必定的同步机制下,就会出现提交顺序的乱序,使得共享数据在内存的不可见性。app
在指令重排序的状况下,只要保证最终的计算结果和严格的串行执行环境下的结果一致下,重排序等优化措施是能够的。函数
多线程中线程间各自的操做执行顺序不一样,强行保持程序的串行性,只会增长线程间的调度次数,而频繁的线程调度会引发不要的上下文操做使得线程的开销很大也下降了程序的执行速度。在只须要进行数据共享的操做内使性能
用同步机制协调线程间的操做,实现线程间的数据共享(保存线程间短暂的串行性)就可以减小不要的性能开销。优化
Java内存模型屏蔽了不一样处理器架构内存模型之间的差别(JVM经过插入内存栅栏来屏蔽JMM与底层平台内存模型之间的差别)spa
1)进行重排序的条件:操做之间不存在偏序关系和全序关系
2)禁止重排序的方法:锁,volatile,final
重排序会破外操做之间的偏序关系(Happens-Before),从而产生数据竞争问题
1)简介:
Java内存模型是经过各类操做来定义的,包括对变量的读写操做,监视器的加锁和释放操做,以及现场的启动和合并操做。
Java内存模型实质是为最终计算结果的正确性,而采起的实现内存操做间保存偏序关系即实现内存操做的有序性,实现内存操做的内存可见性和原子性的内存访问操做的模型
2)偏序关系(Happens-Before):
偏序关系(Happens-Before)的本质:保证操做之间的内存可见性
保持偏序关系(Happens-Before)的准则:
规则名称
内容
说明
程序顺序规则 程序代码顺序天然保持操做间的偏序关系 监视器锁规则 在同一个锁上锁的释放确定在锁的获取以后 volatile规则 volatile变量的写操做确定在volatile变量的读操做以前 线程启动规则 线程的启动操做start确定在线程执行操做以前 线程结束规则 线程执行的任何操做确定在线程检测到该线程结束以前执行,或者从join操做或者调用Thread.isAlive是返回 中断规则 interrupt中断操做必须在线程检测到线程中断以前执行 终结器规则 对象的构造函数必须在对象的终结器执行以前执行 传递性 操做顺序的传递性
不安全发布操做的本质就是:对象的发布操做和对象的访问操做之间缺少偏序关系(Happens-Before排序)
除了不可变对象外,使用被另一个线程初始化的对象一般都是不安全的,除非对象的发布操做是在使用该对象的线程开始使用以前执行(即保存对象的发布操做在对象的加载操做以前也就是两个操做间保持偏序关系)
安装发布对象的本质就是:使用锁或者volatile关键字来禁止操做之间的重排序,从而保持操做之间的偏序关系
初始化的几种模式:
初始化模式 |
优势 |
缺点 |
备注 |
synchronized加锁模式 | 禁止重排序,实现了线程安全的初始化 下降了初始化类或者建立实例的开销 |
数据竞争严重的状况下太耗性能 增长了访问被初始化延迟的字段的开销 |
不推荐使用 |
类初始化加锁模式 | 在类的静态初始化过程完成初始化,实现了线程安全的初始化 下降了初始化类或者建立实例的开销 |
仅适用于在构造时的状态,对于可变的对象读写操做之间仍然须要同步机制。仅限于静态字段的初始化 增长了访问被初始化延迟的字段的开销 |
类初始化期间得到一个锁,而且每一个线程都至少获取一次这个锁以确保这个类已经加载。类初始化(静态初始化)时类在初始化阶段执行,在类型加载后被线程使用以前执行。 |
双重检测锁定模式(DCL) | 下降了因synchronize锁带来的性能消耗 下降了初始化类或者建立实例的开销 |
没有实现线程安全的初始化 | 错误的不安全的延迟初始化方案 |
基于volatile改良后的双重检测锁定模式(VDCL) | 下降了因synchronize锁带来的性能消耗,实现了线程安全的初始化 除了可用于静态字段也能够用于实例字段 下降了初始化类或者建立实例的开销 |
增长了访问被初始化延迟的字段的开销 |
不管经过类初始化加锁模式仍是volatile关键字都是实现了禁止重排序或者实现操做间的偏序关系从而保证了操做内存的可见性。
初始化安全性只能保证final修饰的值从构造过程完成时开始的可见性,对于非final的值,或者在构造完成后能够改变的值,必须采用同步来确保可见性