java线程 -> 线程工作内存 -> 主物理内存

线程工作内存的原理是栈内是连续的小空间,寻址速度比堆快得多,将变量拷贝到栈内生成副本再操作

什么是重排序

代码指令可能并不是严格按照代码语句顺序执行的。
大多数现代微处理器都会采用将指令乱序执行的方法,在条件允许的情况,直接运行当前有能力立即执行的后续指令,避免造成等待(CPU的操作速度远快于与物理内存通信的速度),大大提高执行效率。JIT编译器也会做指令重排序操作。

工作内存

每个线程有一个栈,每个栈有一个工作内存,将共享变量读到工作内存后,线程使用的变量值就是自己栈内存中的变量副本了,此时主内存变量发生修改之后,线程工作内存中的值由于已经加载,不会产生对应的变化。
volatile修饰的变量,虚拟机会保证从主内存加载到工作线程的值是最新的。

as if serial

所有的操作都可以为了优化而进行重排序,但是最终的执行结果就像按顺序执行一样,如果有依赖关系,虚拟机会阻止重排序。
异常处理:java异常处理机制也会为重排序做一些特殊处理,也就是插入错误补偿代码,将程序恢复到发生异常时应有的状态。

内存可见性

为了避免处理器访问主内存的时间开销,处理器大多会利用缓存提高性能,因此,处理器上的缓存和主内存的数据并不是实时同步的,各cpu间缓存的数据也不是实时同步的

happens-before

happens-before原则保证了我们的程序执行的可预测性,内存的可见性

  • 单线程程序代码次序法则
  • 监视器锁法则,对一个监视器锁的解锁 happens-before每一个后续的加锁
  • volatile变量法则
  • 线程启动法则:thead.start() happens-before 线程里面的动作
  • 线程中的动作happen before 线程终结或thread.join
  • 传递性原则

内存屏障

内存屏障 Memory Barrier 是一种CPU指令,用于控制特定条件下的重排序和内存可见性
LoadLoad屏障 -> Load1; LoadLoad; Load2 load2及后续读取操作要读取的数据被访问前,load1要读取的数据被读取完毕
StoreStore屏障 Store2及后续写入操作执行前,保证Store1的写入操作对其他处理器可见(刷新到内存)
LoadStore屏障 load1数据装载,之前于Store及后续的存储指令刷新到内存
StoreLoad屏障 保证Store的写入对所有处理器可见先于load操作。该屏障之前的所有内存访问指令(存储和load)完成之后,才执行屏障后的指令它的开销最大,兼具其他3种内存屏障的功能,因此它开销会很昂贵

内存屏障的作用是禁止重排序,虽然表面上是禁用单线程执行的指令重排序,但是间接影响到多线程之前的指令重排序,如保证变量b被操作前,先于b的写操作一定能被其他线程访问到

Thread A:
 a=1;
 b=true;
 
Thread B:
 if(b){
   c = a + 1;
} 

volatile

当一个操作是volatile写时,与前面的任何类型的读写都不能重排序,但可以与后面的普通读写重排序
当一个操作是volatile读时,与后面任何类型的操作都不能重排序,与前面的普通读写可以重排序
volatile读与写不能重排序
volatile写使用StoreStore内存屏障,保证前面所有的普通写操作已经对任意处理器可见了,因为StoreStore屏障将保障上面所有的普通写在volatile写之前刷新到主内存

当写入一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存
当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效,从主内存中读取所有的共享变量。

class VolatileExample {
	int a = 0;
	volatile boolean flag = false;

	public void writer() {
		a = 1;                   //1
		//StoreStore,a对flag可见
		flag = true;               //2
		//StoreLoad,flag和a对后续可见
	}

	public void reader() {
		//LoadLoad,flag和a可见
		if (flag) {                //3
		//LoadStore,flag和a可见
			int i =  a;           //4
			……
	}
}

编译器如果能证明volatile变量只能被单线程访问,那么就可能会把它作为普通变量处理
使用volatile关键字仅能实现对原始变量(如boolen、 short 、int 、long等)操作的原子性

final

对final语义的扩展保证一个对象的构建方法结束前,所有final成员变量都必须完成初始化(的前提是没有this引用溢出)。

CAS在JDK5中被J.U.C包广泛使用,在JDK6中被应用到synchronized的JVM实现中,因此在JDK5中J.U.C的效率是比synchronized高不少的,而到了JDK6,两者效率相差无几,而synchronized使用更简单、更不容易出错,所以它是专家组推荐的首选,除非需要用到J.U.C的特殊功能(如阻塞一段时间后放弃,而不是继续等待)。

参考文章

http://www.cnblogs.com/mengheng/p/3491092.html

https://tech.meituan.com/MySQL_PingCAP_Practice.html

http://www.cnblogs.com/chenyangyao/p/5269622.html
https://blog.csdn.net/u011663071/article/details/78964991

posted on 2018-12-29 16:58  j.liu windliu  阅读(202)  评论(0编辑  收藏  举报