Java 线程安全问题
2022-09-08 本文已影响0人
Tinyspot
1. 线程安全问题分为多个层面
- 可见性 volatile, Synchronized
- 有序性 volatile, Synchronized
- 原子性 AtomicInteger, Lock, Synchronized
2. 可见性
分析下面代码的运行结果:
public class Demo {
static boolean flag = true;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
while (flag) {}
});
thread.start();
TimeUnit.SECONDS.sleep(1);
flag = false;
}
}
线程并不会停止,进入死循环
原因是读取的是【工作内存】中的缓存,并不会去访问【主存】中的数据
main 线程修改了主存中的数据,对其他线程(thread-0)不可见
改进1: 让线程暂停 1s, 此时可以读取最新值
while (flag) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
}
改进2:static volatile boolean flag = true;
volatile 可解决可见性问题,可让一个线程对共享变量的修改,能够及时的被其他线程看到
2.1 关于可见性与原子性
volatile 只能保证看到最新值,不能解决指令交错
synchronized 即可以保证代码块的原子性,同时也保证代码块内变量的可见性;但 synchronized 属于重量级操作,性能相对更低
3. 有序性
JVM 在不影响正确性的前提下,可以调整语句的执行顺序,这种叫【指令重排】
多线程环境下指令重排会影响正确性
指令重排序优化,前提是不能影响结果
分阶段,分工是提升效率的关键
3.1 禁止指令重排
volatile
4. 原子性
i++
其实是 3 条指令
4. 对象发布和初始化时的安全问题
4.1 处理逸出
- 返回“副本”
- 工厂模式