并发编程基础概念:
原子性: 多个线程一起执行的时候,一个操作开始就不会受到其他的影响.
可见性: 当一个线程修改了某一个共享变量的值, 其他线程能否知道这个修改.
有序性: 在并发时,可能会进行指令重排,导致程序执行顺序发生改变.
核心的问题: 性能的提升!
CPU, 内存, I/O设备的存在严重的速度差异.
根据木桶理论, 程序的整体性能取决于最慢的操作- I/O
为了性能的提升,计算机体系结构,操作系统,编译程序都做成改变
1. CPU提供了缓存,用于均衡和内存的速度差异
2. 操作系统增加了进程,线程,以便于分时使用CPU, 进而均衡了CPU和I/O设备的速度差异.
3. 编译程序优化指令的执行次序,使得缓存能够得到更加合理的应用.
并发容易出现Bug的原因:
1>缓存导致可见性问题:
单核CPU时代, 线程的可见性可以得到保证. 到了多核时代,每个CPU都有自己的缓存,
多个线程在不同CPU操作不同的缓存, CPU缓存和内存数据的一致性难以保证.
2>线程切换带来原子性问题:
操作系统允许多个任务进行切换,支持多进程分时复用在操作系统的发展史上却具有里程碑意义.
早期是基于进程操作CPU,现在是基于线程操作,CPU能保证原子性是CPU级别的指令而不是count+=1
这样的高级语言.所以当时间片结束任务切换时count+=1可能没执行完毕.
3>编译优化带来的有序性问题:
编译器为了优化性能,有时会改变程序中语句的先后顺序.
指令重排可以保证串行语义一致, 但是没有义务去保证多线程间语义一致.