当前位置:   article > 正文

JUC之Atomic_juc包中atomic

juc包中atomic

本文乃个人拙见,如有错误,欢迎指出,不能误导新人

上一节讲到了CAS,最后说到了CAS的缺点,如果有很多写操作的话,CAS的性能会降低,这个时候还是使用synchronized比较合适。但是CAS还存在一个问题就是ABA问题。

ABA问题

之前说到,compareAndSwap,是比较持有值与内存值是否相等,然后进行插入,乍一看没什么毛病,如果A、B两个线程同时取到了主内存中的i=1,这个时候B线程操作比较快,将i写成2,然后写入主内存中,然后这个时候B线程很快速的又修改了一次,将2修改为了1,这个时候A线程调用CAS,发现内存中是1没问题,进行修改,这个时候其实中间就有了猫腻,如果是在栈中修改值的话,t1和t2同时对A进行操作,这个时候t2将栈中元素从A修改为了ABA,而t1线程比较A的时候发现两个值是相等的于是进行操作,这个时候是出问题的。
在这里插入图片描述

解决方案

在JUC中的atomic包中有一个AtomicStampeReference类,这个类在每一次修改值的时候加一个版本号(stamp),就好比svn一样,每一次加一,这样要是t2线程进行修改两次的话,版本号就会变成3,而t1线程在去修改的时候虽然值相同,但是t1期望的版本为1,所以修改失败,

package aba;

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

public class ABADemo2 {

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

    public static void main(String[] args) throws InterruptedException {

        new Thread(() -> {
            atomicInteger.compareAndSet(100,101);
            atomicInteger.compareAndSet(101,100);

            try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }

            atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
            atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
        },"t1").start();

        new Thread(() -> {
            try {
                int stamp = atomicStampedReference.getStamp();
                TimeUnit.SECONDS.sleep(3);
                System.out.println("======atomicInteger cas操作");
                System.out.println(atomicInteger.compareAndSet(100, 2019)+"\t"+atomicInteger.get());
                System.out.println("======atomicStampedReference cas操作");
                System.out.println(atomicStampedReference.compareAndSet(100,102,stamp,stamp+1));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"t2").start();

        TimeUnit.SECONDS.sleep(2);


    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

代码中定义了一个atomicInteger,和一个atomicStampedReference,可以看到的是使用了atomicStampedReference防止了ABA操作。
在这里插入图片描述

AtomicReference

JUC为我们准备了AtomicInteger,AtomicLong…但是这些个类不能同时保证多个变量的原子性,如果我们需要其他类型呢?这个时候就需要AtomicRefrence(原子引用)来解决,在上面的AtomicStampedReference其实就是一个原子引用,可以传入自己的对象,

package aba;

import java.util.concurrent.atomic.AtomicReference;

class User{
    String userName;
    int age;

    public User(String userName, int age) {
        this.userName = userName;
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "userName='" + userName + '\'' +
                ", age=" + age +
                '}';
    }
}

public class ABADemo {

    public static void main(String[] args) {
        User z3 = new User("z3",13);
        User li4 = new User("li4",32);

        AtomicReference<User> atomicReference = new AtomicReference<>();
        atomicReference.set(z3);

        System.out.println(atomicReference.compareAndSet(z3, li4)+"\t"+atomicReference.toString());
        System.out.println(atomicReference.compareAndSet(z3, li4)+"\t"+atomicReference.toString());
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

代码中将一个User类传入,然后直接传入期望类,以及改变的类就可以了。
在这里插入图片描述
在atomic包里面还有一个AtomicMarkableReference,其原理基本和AtomicStampedReference是一样的,AtomicMarkableReference不是用版本号控制的,而是用一个标记true,false来标记。
在这里插入图片描述

最后

AtomicIntegerArray,看这个类的set方法,实现原理基本相同,以及AtomicLongArray

public final void set(int i, int newValue) {
        unsafe.putIntVolatile(array, checkedByteOffset(i), newValue);
    }
  • 1
  • 2
  • 3

最后说四个,DoubleAccumulator,DoubleAdder,LongAccumulator,LongAdder这四个类是干嘛用的?Accumulator计算,这四个是用在高并发下面的,就是很多写操作的时候,他们使用了cell,每个线程都先写在各自的cell中,然后最后做统计,也就是说,在高并发下面这四个类的速度要快于atomic的类,并且基本是用来做统计用的,并不会来单独跟新值,在低并发下面这四个类和atomic类都差不多,这中思想有点象MapReduce了。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/代码探险家/article/detail/996116
推荐阅读
相关标签
  

闽ICP备14008679号