Java并发编程可见性bug和demo示例

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方法是正确示例。

要解决可见性问题,方法之一就是使用同步操作,让多个线程串行累加。