关于影响线程安全的三大问题:可见性,原子性,有序性我们都很了解 一般在讲原子性问题的时候, 会扔出这么一段代码
public class CPUTest {
private int i = 0;
private void incr(){
i++;
}
private void getI(){
System.out.println(i);
}
public static void main(String[] args) throws InterruptedException {
CPUTest cpuTest = new CPUTest();
CountDownLatch cdl = new CountDownLatch(1);
new Thread(){
@Override
public void run() {
try {
cdl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 5000; i++) {
cpuTest.incr();
}
System.out.println("线程1完事");
}
}.start();
new Thread(){
@Override
public void run() {
try {
cdl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 5000; i++) {
cpuTest.incr();
}
System.out.println("线程2完事");
}
}.start();
cdl.countDown();
TimeUnit.SECONDS.sleep(5);
cpuTest.getI();
}
}
结果跑不出10000, 就算有线程安全问题,具体原因一般说这是因为i++不是原子操作,并且有可见性问题。 得到的结论大概如下:
因为i++不是原子操作, 所以在多线程下, 可能a,b两个线程读到的i都是3, a线程计算3+1=4后先将4暂存到工作内存,b线程计算出3+1=4后讲4暂存到工作内存。a线程刷新m到主内存, b线程刷新m到主内存,经过两个i++, 结果i的值只加了1,所以原子操作引发线程安全性问题
是不是很清晰?但我还是有以下问题
- 工作内存在现实中到底是什么?
- 如果工作内存对应的是JMM的CPU缓存,那如果在只有一个CPU的情况下,i++有没有问题? 于是我把代码上传到单核的虚拟机上,与本地做比较:
- 本地跑的结果一直在6000左右
- 在虚拟机下同样代码多次跑出10000 根据这个结果我们可以详细的解释导致i++线程安全性问题的原因
- 在JMM中,CPU缓存大致代表工作内存
- 在单核多线程的情况下,执行i++即使有线程切换,由于操作的是一份工作内存(缓存),所以i++没有问题
- 在多核多线程情况下,才有线程安全性问题