问题:如何提高串行化程序的性能?
首先我们先了解并行的两个定律:
Amdahl定律:表明,我们需要从根本上解决问题,优化在与串行化比重,提高系统并行化模块比重,这样加入合理的cpu个数,才能以最小的投入,得到最大的优化。强调:一味的提高cpu个数对性能并没有改变!
Gustafson定律:并行化比重占的越多,那个增加cpu个数就能实现线性增长。
即两个定律并不矛盾:当串行比列为F=1时,加速比都为1, 当串行化比列为100%时,加速比为n(处理器个数)
回到JMM: 强调,程序往往会出现并发,那么在并发中,如何保证并行部分的程序数据是安全性和一致性。
JMM的关键技术点在多线程的原子性,可见性,有序性。
原子性:指一个操作不可被中断,即使在多线程下,也是不可被干扰,好比32位系统中的long,它的读写并不是原子性的,所有在多线程下,数据很可能会被写乱!
可见性:在并发情况下,系统不能保证并行程序能够察觉到全局变量值的改变,(由于缓存优化或者硬件优化,指令重排等)
有序性:往往一个程序,会根据系统的指令重排的优化导致乱序, 指令重排可以保证串行语义一致,但是并不能保证多线程的语义是否一致
指令重排:c=a+b
在汇编语言中:每一条指令并不能一步走完,需要执行取值IF,译码和取寄存器操作数ID,执行或者有效计算EX,存储器MEM, 写回WB。 所有发明了流水线,但是有一个问题就是,1. a取值 2.b取值,a译码 3.a准备计算,b译码 ====》这个时候a准备计算了,但是b还在译码, 所以导致需要等待一个时间,这样将导致后续所有指令都会等待一个时间。所有有了指令重排,比如现在又有e = f + g 这个时候程序不会等待,而是先取值f这样,就不会导致程序停留啥也不干!
指令重排必须遵循happen-before原则:
1. 程序顺序原则
2. volatile规则:v变量的写先发与读,保证可见性
3. 锁规则:解锁必须发生在加锁后
4.传递性:A->B->C 那么A一定先于C
5.线程的所有操作必须先于线程的终结(Thread.join())
6. 线程的中断先于被中断线程的代码
7.对象的构造函数执行结束先于finalize()
8.线程的start()方法优先与它每一个动作