12.2硬件的效率与一致性
处理器与内存速度矛盾-->
1.引入高速缓存-->新的问题:缓存一致性(Cache Coherence)
2.指令重排优化( Instruction Reorder)
保证结果与顺序执行结果一致,但不保证程序中各个语句计算先后顺序与输入代码的顺序一致.
12.3 Java内存模型( Java Memory Model, JMM)
屏蔽屌各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果.
12.3.3 volatile型变量的特殊规则
1.保证此变量对所有线程的可见性.(当一条线程修改了这个变量的值,新值对于其他线程是可以立刻得知的.)
2.禁止指令重排优化.
对于第二点,有一个非常经典的例子就是DCL (double check lock)的使用.
如下:
public static SingletongetInstance()
{
if (instance == null)
{
synchronized(Singleton.class) { //1
if (instance == null) //2
instance = new Singleton(); //3
}
}
return instance;
}
因为Instruction Reorder的关系,其中的instance=new Singleton ()会出一点问题:
这句假设分为三步
1.先申请内存 2.构造Singleton 3.将instance指向新的内存区域
如果不进行指令重排,这个是没问题的.
如果指令重排后执行顺序是. 1 3 2.这就导致执行3后,instance已经非null,此时若恰好有别的线程重新访问get_instance函数.将得到instance非null的结果,并此时返回一个还没执行完构造函数的instance实例.从而出错.
但是volatile关键字的第二个语义,便是‘禁止指令重排优化’.
因此,如果把instance变量声明为 volatile , 双重检测法似乎也是work的.
如下:
private static volatile Singleton instance;
12.3.5 原子性可见性 有序性
12.3.6 先行发生原则
1.程序次序规则 (控制流顺序)
2.管程锁定规则 (同一个锁中unlock先于下一个lock)
3.volatile变量规则 (写操作先于读操作)
4.线程启动规则
5.线程终止规则
6.线程中断规则
7.对象终结规则
8.传递性
12.4 Java与线程
13.2 线程安全
13.2.1五类划分
13.2.2实现线程安全的方法
1互斥同步
Synchronize关键字
1. synchronize同步块对同一线程是可重入的,不会锁死自己
2. 同步块在已进入的线程执行完之前,会阻塞后面的其他线程进入
3. 阻塞或唤醒线程需要从用户态转到和心态,因此可能耗费很多时间, synchronize是一个重量级(Heavyweight)的操作
2 非阻塞同步
13.3锁优化
1.自旋锁与自适应自旋(忙循环以避免挂起,恢复线程)
2.锁消除 (虚拟机的优化)
3.锁粗化 (避免频繁lock与unlock)
4.轻量轻锁
5.偏向锁