Juc并发编程10——原子类与ABA问题解决方案(上)

简介: 除了加锁以外,还可以使用原子类实现操作原子性。它底层采用CAS算法,使用简单、性能高效、线程安全

除了加锁以外,还可以使用原子类实现操作原子性。它底层采用CAS算法,使用简单、性能高效、线程安全。


简单示范下它的使用。

public class Demo24 {
    public static void main(String[] args) {
        AtomicInteger integer = new AtomicInteger(1);  // 不能向Integer一样自动装箱(Integer i = 1)
        System.out.println(integer.getAndIncrement()); //相当于i++
        System.out.println(integer.incrementAndGet()); //相当于++i
    }
}

如何验证它的原子性呢?看看下面的代码。

public class Demo25 {
    public static void main(String[] args) throws InterruptedException {
        AtomicInteger integer = new AtomicInteger();
        Runnable r = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 1000000; i++) {
                    integer.getAndIncrement();
                }
                System.out.println("thread finish...");
            }
        };
        new Thread(r).start();
        new Thread(r).start();
        TimeUnit.SECONDS.sleep(1);
        System.out.println(integer.get());
    }
}

运行输出的结果如下。确实是原子性的哦。


thread finish...
thread finish...
2000000


为什么会这么神奇呢?我们来阅读下源码一探究竟吧。

private volatile int value;
  public AtomicInteger(int initialValue) {
        value = initialValue;
   }
   public AtomicInteger() {
   }

它使用volatile关键字修饰了value,这样在CAS操作时就不会出错。

private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;
    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

在静态块中,通过计算偏移地址,获取value相对于对象的偏移地址,这样就可以直接在对应内存对数据进行操作。


接着看看自增操作是如何完成的。

public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

到Unsafe中看看。下面的参数列表中var1是数组或者对象(要修改的值是数组的元素或者对象的属性),var2是offset偏移地址,var4是delta,参数变化量。可以看到,它是一个do-while循环,如果CAS失败会重新尝试,一直到成功。

public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2); // 以Volatile形式读取变量的值
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); //对变量进行CAS操作
        return var5;
    }


为了帮助大家更加深入理解,我举一个例子。比如线程1,2同时对变量A进行修改,线程1速度较快,读到它的值是1,准备CAS操作,线程2此时也开始读到数据是1,但是进行CAS操作时线程1在执行,因此它无法CAS成功。当线程1执行完CAS操作后,数据A的值变成2。此时线程2尝试下一轮获取变量的值就是2(变量通过volitile修饰了),然后进行CAS操作值就变成了3.不过由于是do-while循环,var5的值仍然是2,getAndAddInt返回的参数是2,因此也符合getAndIncrement的逻辑定义(先获取值再自增)。


用图片描述下。

如果是incrementAndGet的话,其底层逻辑会不会是while循环呢?答案是否,大家看看源码。


public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }

原来是在原来的return的值基础上+1,这样就可以复用一个底层方法getAndAddInt实现两个不同的逻辑。这个思想值得大家学习呀。

可见,原子类底层也是采用CAS算法保证的操作原子性,并且它提供了compareAndSet直接给外部使用。

    // 第一个值是期望值,第二个值是要更新后数据,符合期望值expect就会更新数据为update
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

写个demo测试下

public class Demo26 {
    public static void main(String[] args) {
        AtomicInteger integer = new AtomicInteger(10);
        integer.compareAndSet(20, 15); // fail
        System.out.println(integer);
        integer.compareAndSet(10, 30); // success
        System.out.println(integer);
    }
}


相关文章
|
2月前
|
安全 Java API
JAVA并发编程JUC包之CAS原理
在JDK 1.5之后,Java API引入了`java.util.concurrent`包(简称JUC包),提供了多种并发工具类,如原子类`AtomicXX`、线程池`Executors`、信号量`Semaphore`、阻塞队列等。这些工具类简化了并发编程的复杂度。原子类`Atomic`尤其重要,它提供了线程安全的变量更新方法,支持整型、长整型、布尔型、数组及对象属性的原子修改。结合`volatile`关键字,可以实现多线程环境下共享变量的安全修改。
|
6月前
|
缓存 安全 Java
深入理解Java并发编程:线程安全与锁优化
【5月更文挑战第27天】 在Java并发编程中,线程安全和性能优化是两个核心议题。本文将深入探讨如何在保证线程安全的前提下,通过合理使用锁机制来提升程序性能。我们将从基本的同步关键字出发,逐步介绍更高级的锁优化技术,包括可重入锁、读写锁以及乐观锁等,并配以实例代码来展示这些技术的应用。
|
6月前
|
安全 Java
JUC并发编程之原子类
并发编程是现代计算机应用中不可或缺的一部分,而在并发编程中,处理共享资源的并发访问是一个重要的问题。为了避免多线程访问共享资源时出现竞态条件(Race Condition)等问题,Java提供了一组原子类(Atomic Classes)来支持线程安全的操作。
|
6月前
|
缓存 算法 Java
JUC并发编程之CAS
CAS,即Compare and Swap,是一种并发编程中用于实现多线程环境下的原子操作的技术。它是一种无锁算法,用于解决多线程环境下的数据同步问题。CAS操作包含三个操作数:内存位置V,旧的预期值A和即将要写入的新值B。只有当内存位置的值与旧的预期值A相等时,才会将新值B写入内存位置V,否则不执行任何操作。CAS操作是原子的,保证了多线程环境下的数据一致性和线程安全性。
|
安全 Java
并发编程系列教程(11) - 原子类
并发编程系列教程(11) - 原子类
43 0
|
设计模式 安全 Java
JUC第十二讲:JUC锁 - 看不懂锁核心类 AQS 原理来打我
JUC第十二讲:JUC锁 - 看不懂锁核心类 AQS 原理来打我
|
存储 SpringCloudAlibaba 安全
JUC并发编程(四):synchronized底层原理和锁升级优化
`synchronized`翻译过来是**同步**的意思,它是Java中一个关键字,是JVM层面提供的同步锁机制,用于保证多线程访问同一资源的可见性、互斥性。即当一个线程已经获取资源锁时,其他试图获取的线程只能等待或者阻塞在那里。
91 0
JUC并发编程(四):synchronized底层原理和锁升级优化
|
算法 安全 Java
【Java并发编程 八】JUC并发包下原子类
【Java并发编程 八】JUC并发包下原子类
85 0
|
安全 Java 调度
Java多线程之CAS中的ABA问题与JUC的常见类
Java多线程之CAS中的ABA问题与JUC的常见类
123 0
Java多线程之CAS中的ABA问题与JUC的常见类
JUC并发编程学习(九)-读写锁
JUC并发编程学习(九)-读写锁
JUC并发编程学习(九)-读写锁