一、线程安全
什么是线程安全
如果多线程环境下代码运行的结果是符合预期的,则说这个程序是安全的
线程不安全的原因
- 线程调度的随机性
- 修改共享数据
即多个线程针对某一个共享变量进行修改. - 原子性
指令不可分割 - 内存可见性
在多线程中,某一线程对数据的操作,其他线程能否及时看见 - 指令重排序
指Jvm,编译器会对代码进行优化,可能会改变代码的执行顺序
二、解决方法
1.Synchronized关键字
a.实现原理
基于操作系统的mutex互斥锁
b.特点
-
互斥性
通过加锁,当多个线程同时竞争同一资源时,同一时间内只有一个线程会拿到这把锁,而未抢上锁的线程会进行阻塞等待,直到该线程释放锁后,由操作系统唤醒阻塞线程中的某一个.通过互斥达到原子性 -
内存可见性
当一个线程进行加锁后,会从主内存拷贝变量的副本到工作内存,代码执行完后,再将工作内存的值更新到主内存,然后释放锁 -
指令重排序
synchronized 对指令重排序有一定约束作用,但并不能完全禁止
2.Volatile关键字
a.实现原理
通过插入内存屏障,强制对缓存的修改操作即刻写入主存;确保指令重排序时不会把其后面的指令排到内存屏障之前,也不会把前面的指令排到内存屏障后
b.特点
- 不保证原子性
- 内存可见性(强制)
Volatile修饰的变量,读取变量时,会从主内存中读取该变量的最新值到工作内存,再从工作内存中读取副本; 写入变量时,先改变变量副本的值,然后再强制将改变后副本的值从工作内存刷新到主内存。
- 禁止指令重排序
被volatile修饰的变量,编译器不会再进行优化
三.总结
区别 | Synchronized | Volatile |
---|---|---|
原子性 | 可以保证 | 不能保证 |
内存可见性 | 可以保证 | 可以保证 |
指令重排序 | 有一定约束 | 禁止 |
修饰 | 方法和代码块 | 变量 |
是否引发线程阻塞 | 会 | 不会 |