文章目录
- 重排序
- 数据依赖性
- as-if-serial
- 重排序对多线程的影响
- 顺序一致性
- 同步程序的顺序一致性效果
- 同步/异步
- 总线事务
- 双重校验锁
——————————————————————————————————
重排序
数据依赖性
数据依赖不能进行重排序
as-if-serial
- as-if-serial语义的意思是:不管怎么重排序(编译器和处理器为了提高并行度),(单线程)程序的执行结果不能被改变。
- 【as-if-serial不允许指令重排序是要让结果不变,不是严格意义上的不允许重排序】
重排序对多线程的影响
class ReorderExample {
int a = 0;
boolean flag = false;
public void writer() {
a = 1; // 1
flag = true; // 2
}
Public void reader() {
if (f?lag) { // 3
int i = a * a; // 4
……
}
}
}
可能出现,2->3->4->1这样的执行顺序,导致结果i=0;
顺序一致性
- 不让重排序
- 顺序一致性指多线程下排队执行,只会出现写后读的情况。(看到的和执行的一样)
- 顺序一致性内存模型有两大特性:
- 1)一个线程中的所有操作必须按照程序的顺序来执行。
- 2)(不管程序是否同步)所有线程都只能看到一个单一的操作执行顺序。在顺序一致性内存模型中,每个操作都必须原子执行且立刻对所有线程可见。
同步程序的顺序一致性效果
通过加锁实现顺序一致性,加锁后里面重排序也没有问题,因为上锁之后,切换线程后t1没有释放锁,t2无法拷贝,时间片过了之后就会切换成t1执行,t1整个方法执行完之后才会释放锁,肯定能保证t1的结果正确。
同步/异步
- 同步:排队;共享资源,一个一个操作资源
- 异步:不排队;一个线程没操作完,其他线程也可以使用该资源
总线事务
总线事务包括读事务和写事务(事务:一系列的操作要么都成功,要么都失败-ACID)
- 读事务从内存传送数据到处理器
- 写事务从处理器传送数据到内存
- 每个事务会读/写内存中一个或多个物理上连续的字
- 关键: 总线会同步试图并发使用总线的事务
在一个处理器执行总线事务期间,总线会禁止其他的处理器和I/O设备执行内存的读/写
- 总线中电压会互相干扰,所以前面必须实现拦截(物理限制,导线传输电压互相有影响)
- 如果以后变成光纤传输就不会有电压影响了,光信号支持双向传输也支持同时传输信号(例:人眼一次性能看到很多物体,不同物体光波不一样)
当单个内存操作不具有原子性时,可能会产生意想不到后果:
- 因为=》总线传输一个指令时,不止传输一次,会传输很多次(总线宽度是36-41位,意味着单次传输只能传输41bit数据)
- 指令的数据传输都是基本类型数据,必须是8的倍数。(就像传long类型的数据需要分两次传输,因此传输数据都是一批次一批次的,不是单独传的,否则数据会混乱)
双重校验锁
双重检查锁定:人们想通过双重检查锁定来降低同步的开销。下面是使用双重检查锁定来实现延迟初始化的示例代码。
构造方法还是有问题的,还有重排序问题
现在不锁方法了,证明方法可以被很多线程拷贝
- 双检锁/双重校验锁(DCL,即double-checked locking)
- JDK 版本:JDK1.5起
- 是否Lazy初始化:是
- 是否多线程安全:是
- 实现难度:较复杂
- 描述:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
- getInstance()的性能对应用程序很关键。
【实例】
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}