第一章:走进并行世界
1.1 并行计算的历史
1.2 概念
-
同步:同步方法一旦开始,调用者必须等到方法调用返回后才能继续后续的行为;
-
异步:异步方法通常会在另一个线程中“真实”地执行,整个过程不会阻碍调用者的工作。
-
并发:多个任务交替进行(但对外部观察者来说看起来就行多个任务“同时进行”);
-
并行:多个任务同时进行。
-
临界区:公共资源(共享数据)。
-
阻塞:一个线程被挂起,等待其他线程释放临界区资源,在等待的时候就是被阻塞状态;
-
非阻塞:与“阻塞”相反,只某个线程没有受到任何阻碍,可以执行。
-
死锁:持有资源的多个线程互相等待对方释放资源;
-
饥饿:指某一个或多个线程因为种种原因无法获得所需要的资源,导致一直无法执行;
-
活锁:多个线程同时主动释放自己的资源给对方,导致资源不停地在多个线程之间跳动,但没有任何一个线程拿到了完整的资源。
1.3 并发级别
并发的级别有:阻塞、无饥饿、无障碍、无锁、无等待。
1.4 有关并行的两个重要定律
1.4.1 Amdahl定律
加速比定义:
加速比 = 优化前系统耗时 / 优化后系统耗时
加速比 = 优化前系统耗时 / 优化后系统耗时 = 1 / (F+1/n(1-F))
结论:根据Amdahl 定律,使用多核CPU 对系统进行优化,优化的效果取决于CPU的数量,以及系统中串行化程序的比例。CPU 数量越多,串行化比例越低,则优化效果越好。仅提高CPU 数量而不降低程序的串行化比例,也无法提高系统性能。
1.4.2 Gustafson定律
Gustafson 定律也试图说明处理器个数、串行化比例和加速比之间的关系,但是Gustafson 定律和Amdahl 定律的角度不同。
结论:从Gustafson 定律中,我们可以发现,如果串行化比例很小,并行化比例很大,那么加速比就是处理器的个数。只要不断地累加处理器,就能获得更快的速度。
1.4.3 俩公式是否矛盾
Amdahl 定律和Gustafson 定律的结论不同,这是这两个定律对同一个客观事实从不同的角度去审视的结果,他们的侧重点有所不同。
- Amdahl 强调:当串行化比例一定时,加速比是有上限的,不管你堆叠多少CPU参与计算,都不能突破上限!
- Gustafson 定律关心的是:如果可被串行化的代码所占比例足够大,那么加速比就能随着CPU的数量线性增长。
1.5 JMM
概念
- 原子性:指一个操作是不可中断的;
- 可见性:指当一个线程修改了某一个共享变量的值时,其他线程是否能够立即知道这个修改;
- 有序性:指程序在执行时,可能会进行指令重排,重排后的指令与原指令的顺序未必一致。
1.5.4 Happen-Before规则
指令重排不可违背的规则:
- 程序顺序原则:一个线程内保证语义的串行性;
- volatile规则:volatile变量的写先于读发生,这保证了volatile变量的可见性;
- 锁规则:解锁(unlock)必然发生在随后的加锁(lock)前;
- 传递性:A先于B,B先于C,那么A必然先于C;
- 线程的start()方法先于它的每一个动作;
- 线程的所有操作先于线程的终结(Thread.join());
- 线程的中断(interrupt())先于被中断线程的代码;
- 对象的构造函数执行、结束先于finalize()方法。
这些原则都是为了保证指令重排不会破坏原有的语义结构。