Java并发编程导致bug的因素有三个:原子性、可见性和有序性。
我们这里用一个demo来看一下可见性的问题。
可见性问题在单核时代是不存在的,因为所有线程都是运行在一个cpu内核上,一个线程修改了cpu缓存中共享变量的值,其他线程都是可见的。
多核时代,每颗 CPU 都有自己的缓存,多个线程在不同的CPU内核上运行,看到的CPU缓存是各自的CPU缓存,这时多线程修改共享变量,就会出现可见性问题。
在可见性问题上,最常见的例子就是多个线程同时累加一个变量,看最终结果是否是预期结果,这里我们用两个线程并发累加一个共享变量count,每个线程累加100000次。
**
* 并发计算错误示例
*/
public class CountErrorDemo {
private volatile long count = 0;
public static void main(String[] args) throws InterruptedException {
CountErrorDemo ced = new CountErrorDemo();
Thread t1 = new Thread(() -> {
ced.add();
});
Thread t2 = new Thread(() -> {
ced.add();
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("非同步计算:" + ced.count);
ced.count = 0;
Thread t3 = new Thread(() -> {
ced.add2();
});
Thread t4 = new Thread(() -> {
ced.add2();
});
t3.start();
t4.start();
t3.join();
t4.join();
System.out.println("同步计算:" + ced.count);
}
public void add() {
int i = 0;
while(i<100000) {
count +=1;
i++;
}
}
public synchronized void add2() {
int i = 0;
while(i<100000) {
count +=1;
i++;
}
}
}
运行结果:
非同步计算:163373
同步计算:200000
add方法是错误示例,add2方法是正确示例。
要解决可见性问题,方法之一就是使用同步操作,让多个线程串行累加。