目录
1.什么情况下会产生线程不安全?
多个线程执行共享变量的操作:至少一个线程写,线程不安全
2.线程不安全的原因?
- 原子性:多行指令是不可拆分的最小执行单位,就具有原子性
- 可见性:jmm为Java程序屏蔽了不同硬件及操作系统访问内存的差异
- 代码顺序性/有序性:执行机器码指令,都有可能为了提高执行效率,使用指令重排序的方法执行
3.如何解决线程不安全的问题?
- 某个线程,对共享变量的写操作,可以加锁来保证线程安全。
- synchronized关键字
- Lock:锁的接口,它的实现类提供了锁这样的对象,可以调用方法加锁/释放锁
2.如果某个线程,对共享变量是读操作,使用volatile关键字就可以保证线程安全
- volatile关键字是修饰变量的,变量的读操作本身就具有原子性,volatile作用是保证可见性,有序性,所以整个结合就可以满足线程安全。
方式一:synchronized关键字
作用:使用同一个对象进行加锁(基于对象头加锁),可以实现线程间的同步互斥
同步:多个线程,按序执行同步代码
互斥:多个线程间,执行同步代码具有互斥的特点
原理:
- 互斥:满足原子性,某个线程执行同一个对象加锁的同步代码,排斥另一个线程加锁,满足最小执行单元
- 刷新内存:synchronized结束释放锁,会把工作内存中的数据刷新到主存,其他线程申请锁,获取的始终是最新数据
- 有序性:某个线程执行一段同步代码,不管如何重排序,过程中不可能有其他线程执行的指令
其他特征:
可重入——同一个线程可以多次申请同一个锁。
方式二:volatile关键字
- 语法:修饰某个变量(实例变量,静态变量)、
- 作用:保证可见性,多个线程对同一变量的操作,具有可见性 禁止指令重排序,建立内存屏障
- 使用场景:共享变量读操作;常量赋值操作
若某些即存在读,也存在写,可以在写操作加锁,读操作使用volatile