同一进程中多个线程共享该进程中全部的系统资源。
多个线程访问同一共享资源的时候会产生冲突
顺序性、可见性、原子性
顺序性:
程序按照代码的先后顺序执行。
CPU为了提高程序整体的执行效率,可能会对代码进行优化,按更高效的顺序执行。
例如这几行代码:
int ii=10;
ii = 5;
ii = ii +10;
ii = 100;
CPU会优化为:
int ii = 100;
CPU虽然不保证完全按照代码的顺序执行,但它会保证最终的结果和按代码顺序执行时的结果一致。
可见性:
线程操作共享变量时,会将该变量从内存加载到CPU缓存中,修改该变量后,CPU会立即更新缓存,但不一定会立即将它写会内存。
这时候,如果其它线程访问该变量,从内存中读到的是旧数据,而非第一个线程操作后的数据。这肯定不是我们想要的。
当多个线程并发访问共享变量时,一个线程对共享变量的修改,其它线程能够立即看到。
原子性:
CPU执行指令:读取指令、读取内存、执行指令、写回内存。
例如 i++
1 从内存读取i的值
2 把i+1
3 把结果写回内存
这3个步骤也不是一气呵成的,中间可能会被打断,例如某个线程在执行其中一个步骤的时候,如果自己CPU时间片用完了,就会被操作系统切换出去,当这个线程再次获得时间片的时候,黄花菜都凉了。
原子操作的意思是,一个操作(有可能包含多个步骤)要么全部执行,要么全部都不执行。
如何保证线程安全?
- volatile 关键字 //不行
- 原子操作(原子类型) //可以
- 线程同步(锁) //可以
volatile关键字:
- 保证内存变量的可见性
- 禁止代码优化(重排序)
volatile关键字只解决了内存可见性的问题,这是不够了,不能解决线程安全。
需要原子操作或者线程同步才可以保证线程安全。