原子操作原理

处理器实现原子操作

  • 使用总线锁保证原子性
    我们知道处理器存在内存与缓存,当线程读取数据时,先会去取缓存中的数据,若没有再去内存中拿取数据,这样就会发生缓存中与内存中的数据不一致,从而导致不正确的结果。
    为了得到正确的结果,可以在总线上加锁,也就是在内存与缓存上都加锁,也就是给处理器一个#LOCK信号,这样线程就可以独占锁

  • 使用缓存锁保证原子性
    锁的粒度减少

Java实现原子操作

  • 使用cas操作来实现锁
/**
 * 原子操作
 *
 * @author wangmj
 * @since 2018/12/26
 */
public class Counter {

    private int i = 0;
    private AtomicInteger atomicI = new AtomicInteger(0);

    public static void main(String[] args) throws InterruptedException {
        final Counter counter = new Counter();
        List<Thread> ts = new ArrayList<>();
        for (int j = 0; j < 100; j++) {
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int k = 0; k < 10000; k++) {
                        counter.unsafeCount();
                        counter.safeCount();
                    }
                }
            });
            ts.add(t);
        }
        System.out.println(ts.size());
        for (Thread t : ts) {
            t.start();
        }
        //等待所有线程执行完成
        for (Thread t : ts) {
            t.join();
        }

        System.out.println(counter.i);
        System.out.println(counter.atomicI.get());
    }


    private void unsafeCount() {
        i++;
    }

    private void safeCount() {
        while (true) {
            int i = atomicI.get();
            boolean compareAndSet = atomicI.compareAndSet(i, ++i);
            if (compareAndSet) {
                break;
            }else {
                System.out.println("发生并发访问i="+i);
            }
        }
    }
}

结果如下:

998944
1000000

可以看到没有cas操作的发生了错误

  • cas操作三大问题
    • ABA问题
      当检查值发生多次变化,并且最终变回原结果(A-B-A),则发生ABA问题,此时通常多添加一个版本号来标识(A1-B2-A3),同时比较值与版本号,就可以避免ABA(AtomicStampedReferenc类)
    • 循环开销大
      自旋CAS若长时间不成功,则会造成CPU内存大量消耗
    • 只能保证一个共享变量CAS操作
      当多个共享操作同时CAS时,可以将多个共享变量组合在一个对象内,并用AtomicReference保证对象的原子性

猜你喜欢

转载自blog.csdn.net/wmj765/article/details/85312806
今日推荐