CAS / ABA

CAS / ABA

标签(空格分隔): 操作系统


1. CAS 解决 Volatile 不保证原子性的问题

/**
 * Atomically increments by one the current value.
 *
 * @return the previous value
 */
public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
}

public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

    return var5;
}

Volatile不保证原子性的解决方案, 在其中我们可以看到我们通过atomicInteger.getAndIncrement(), 获取值并且将其+1, 重点在于this.compareAndSwapInt(var1, var2, var5, var5 + var4) 通过乐观锁的方法, 相当于实现了原子性的操作.

2. CAS 导致的 ABA 问题解决

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * CAS会导致ABA问题:
 *     CAS算法实现一个重要前提需要取出内存中某时刻的数据并且在当下时刻进行比较并替换(Compare And Set), 那么在这个时间差的时候可能会导致数据的变化.
 *     比如: 一个线程one从内存位置中V中取出A, 这个时候另一个线程two也从内存中取出A. 此时线程one和two都持有位置V中A的备份, 在这个时候two将A->B->A 并写入主存当中.
 *     线程one去进行CAS操作的时候, 发现内存中仍然是A, 然后线程one操作成功.
 *
 *   这个问题主要看线程two对中间操作对 线程one有影响没, 如果有影响的话. 需要解决一下CAS问题.
 */
public class Test {

    private static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
    private static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);

    public static void main(String[] args) {
        /*         ABA问题产生实例 Start */
        new Thread(()->{
            atomicReference.compareAndSet(100,101);
            atomicReference.compareAndSet(101,100);
        },"t1").start();

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicReference.compareAndSet(100, 2019)+"\t"+atomicReference.get());
        }).start();
        /* ABA问题产生实例 End */



        /* ABA问题解决实例 Start */
        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "\t  第一次版本号 : " + atomicStampedReference.getStamp());
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            atomicStampedReference.compareAndSet(100, 101, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + "\t  第二次版本号 : " + atomicStampedReference.getStamp());
            atomicStampedReference.compareAndSet(101, 100, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + "\t  第三次版本号 : " + atomicStampedReference.getStamp());
        }, "t3").start();

        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "\t  第一次版本号 : " + atomicStampedReference.getStamp());
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("是否修改成功: " + atomicStampedReference.compareAndSet(100, 2019, 1, atomicStampedReference.getStamp() + 1) + "\t 当前的stamp值: " + atomicStampedReference.getStamp());
            System.out.println("当前最新值: " + atomicStampedReference.getReference());
        }, "t4").start();
        /* ABA问题 解决实例 End */

    }
}

posted @ 2019-10-11 17:44  X-POWER  阅读(227)  评论(0编辑  收藏  举报