10个线程,每个线程执行1000次i++的结果(微众银行一面)

157 阅读2分钟

一个volatile关键字i,10个线程每个线程执行1000次i++后i的结果(微众银行一面)

volatile不保证原子性

public class TestVolatile {

    private static volatile int count = 0;

    public static void main(String[] args) throws InterruptedException {
        TestVolatile testVolatile = new TestVolatile();
        for (int i = 0; i < 10; i++) {
            new Thread() {
                @Override
                public void run() {
                    for (int j = 0; j < 1000; j++) {
                        testVolatile.increase();
                    }
                }
            }.start();
        }
        //这里为了保证上面的线程执行完再打印结果
        Thread.sleep(3000);
        System.out.println(count);
    }

    public void increase() {
        count++;
    }
}

发现怎么测试,结果都小于等于10000.原因是volatile不保证原子性。最容易想到的就是给increase方法加锁,当然还有种方法是用AtomicInteger的getAndIncrement()方法

image.png

加锁

public synchronized void increase() {
    count++;
}

使用AtomicInteger的getAndIncrement()方法

public class TestVolatile {

    private static AtomicInteger count = new AtomicInteger();

    public static void main(String[] args) throws InterruptedException {
        TestVolatile testVolatile = new TestVolatile();
        for (int i = 0; i < 10; i++) {
            new Thread() {
                @Override
                public void run() {
                    for (int j = 0; j < 1000; j++) {
                        testVolatile.increase();
                    }
                }
            }.start();
        }
        Thread.sleep(3000);
        System.out.println(count);
    }

    public void increase() {
        //getAndIncrement()可保证原子性
        count.getAndIncrement();
    }
}

点进getAndIncrement()方法的源码,可以看到getAndIncrement()底层调用了native类unsafe.getAndAddInt(),那getAndAddInt是如何保证原子性的呢?往下看:

public final int getAndIncrement() {
    //this是当前对象,valueOffset是内存偏移量,直接从内存地址拿到变量值, 然后将该变量值加1
    return unsafe.getAndAddInt(this, valueOffset, 1);
}

点开getAndAddInt方法,可以看到是用的do while循环(自旋锁)+ CAS(Compare And Swap)来保证原子性

public final int getAndAddInt(Object obj, long offset, int delta) {
int v;
do {
    //通过对象和偏移量获取变量的值
    v = this.getIntVolatile(obj, offset);
    /*
    while中的compareAndSwapInt方法尝试修改v的值,具体地, 该方法也会通过obj和offset获取变量的值

    如果这个值和v不一样, 说明其他线程修改了对应改地地址处的值, 此时compareAndSwapInt返回false, 继续循环

    如果这个值和v一样, 说明没有其他线程修对应改地址处的值, 此时可以将对应改地处的值改为v+delta(该变量值加1), compareAndSwapInt返回true, 则退出循环

    Unsafe类中的compareAndSwapInt方法是原子操作, compareAndSwapInt方法不会被其他线程中断
    */
} while(!this.compareAndSwapInt(obj, offset, v, v + delta));
    return v;
}