为什么需要多线程?
大家都知道,CPU、内存、I/O 设备的速度是有极大差异的,为了合理利用 CPU 的高性能,平衡这三者的速度差异,计算机体系结构、操作系统、编译程序都做出了贡献,主要体现为:
- CPU 增加了缓存,以均衡与内存的速度差异;// 导致
可见性
问题- 操作系统增加了进程、线程,以分时复用 CPU,进而均衡 CPU 与 I/O 设备的速度差异;// 导致
原子性
问题- 编译程序优化指令执行次序,使得缓存能够得到更加合理地利用。// 导致
有序性
问题
线程不安全的示例
如果多个线程对一个共享数据进行访问,而且不采取同步操作,那么结果有可能就会不一样。
并发出现问题的根源: 并发三要素
可见性、原子性、有序性。
1、CPU缓存引起
//线程A执行的代码 int m = 0; m = 100;//线程B执行的代码 n = m;
例如线程A对应的CPU1,线程B对应的CPU2。当线程A读取到m = 100;把m的值取到CPU1的高速缓存当中,然后赋值为100,却没有立即写入主内存。
此时线程B先去读主内存中m的值为0,加载到CPU2的高速缓存中。
这就是可见性的问题,线程B未读取到线程A修改的m值。
2、分时复用
int m = 0;// 线程A执行 m += 1;// 线程B执行 m += 1;
这里的m += 1是三条CPU指令:
将变量m从内存中取出来放到CPU寄存器中,然后在CPU寄存器中执行m+1,最后将m的结果写回到内存中。
由于CPU分时复用存在,线程A执行了第一条指令后,线程B执行了三条指令,再切换到线程A执行后面两条指令,造成写入内存的值是2而不是3。
3、 重排序
int m = 0; boolean state = false; m = 10; //语句1 state = true; //语句2
上面定义了一个int类型和一个boolean类型,然后赋值,从代码顺序上来看,应该是先执行完语句1再执行语句2,但是JVM在执行这段代码的时候不一定会按照代码的编写顺序运行。这里可能会发生指令重排(Instruction Reorder)。
在执行时提高性能,编译器和处理器通常会对指令重新排序,重排序分为三种类型:
①编译器优化重新排序
②指令集并行重排序
③内存系统重排序
未完待续。。。