Juc15_基本AtomicInteger、数组、引用AtomicStampedReference、对象的属性修改原子类AtomicIntegerFieldUp 、原子操作增强类LongAdder

文章目录

①. atomic是什么?

  • ①. atomic是原子类,主要有如下:
    Juc15_基本AtomicInteger、数组、引用AtomicStampedReference、对象的属性修改原子类AtomicIntegerFieldUp 、原子操作增强类LongAdder
  • ②. Java开发手册中说明:

Juc15_基本AtomicInteger、数组、引用AtomicStampedReference、对象的属性修改原子类AtomicIntegerFieldUp 、原子操作增强类LongAdder

②. 基本类型原子类(AtomicInteger、AtomicBoolean、AtomicLong)

  • ①. 常用API简介
方法 解释
public final int get() 获取当前的值
public final int getAndSet(int newValue) 获取到当前的值,并设置新的值
public final int getAndIncrement() 获取当前的值,并自增
public final int getAndDecrement() 获取到当前的值,并自减
public final int getAndAdd(int delta) 获取到当前的值,并加上预期的值
public final int incrementAndGet( ) 返回的是加1后的值
boolean compareAndSet(int expect,int update) 如果输入的数值等于预期值,返回true
  • ②. AtomicInteger解决 i++ 多线程下不安全问题
    CountDownLatch如何在程序中使用
public class AtomicIntegerDemo {
    AtomicInteger atomicInteger=new AtomicInteger(0);
    public void addPlusPlus(){
        atomicInteger.incrementAndGet();
    }
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch=new CountDownLatch(10);
        AtomicIntegerDemo atomic=new AtomicIntegerDemo();
        // 10个线程进行循环100次调用addPlusPlus的操作,最终结果是10*100=1000
        for (int i = 1; i <= 10; i++) {
            new Thread(()->{
               try{
                   for (int j = 1; j <= 100; j++) {
                       atomic.addPlusPlus();
                   }
               }finally {
                   countDownLatch.countDown();
               }
            },String.valueOf(i)).start();
        }
        //(1). 如果不加上下面的停顿3秒的时间,会导致还没有进行i++ 1000次main线程就已经结束了
        //try { TimeUnit.SECONDS.sleep(3);  } catch (InterruptedException e) {e.printStackTrace();}
        //(2). 使用CountDownLatch去解决等待时间的问题
        countDownLatch.await();
        System.out.println(Thread.currentThread().getName()+"\t"+"获取到的result:"+atomic.atomicInteger.get());
    }
}
  • ③. AtomicBoolean可以作为中断标识停止线程的方式
//线程中断机制的实现方法
public class AtomicBooleanDemo {
    public static void main(String[] args) {
        AtomicBoolean atomicBoolean=new AtomicBoolean(false);

        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"\t"+"coming.....");
            while(!atomicBoolean.get()){
                System.out.println("==========");
            }
            System.out.println(Thread.currentThread().getName()+"\t"+"over.....");
        },"A").start();

        new Thread(()->{
            atomicBoolean.set(true);
        },"B").start();
    }
}
  • ④. AtomicLong的底层是CAS+自旋锁的思想,适用于低并发的全局计算,高并发后性能急剧下降,原因如下:N个线程CAS操作修改线程的值,每次只有一个成功过,其他N-1失败,失败的不停的自旋直到成功,这样大量失败自旋的情况,一下子cpu就打高了(AtomicLong的自旋会成为瓶颈)
    在高并发的情况下,我们使用LoadAdder

③. 数组类型原子类 (AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray)

  • ①. 数组类型原子类,主要有三个AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
    (了解即可)

  • ②. 代码展示

public class AtomicIntegerArrayDemo {
    public static void main(String[] args) {
        //(1). 创建一个新的AtomicIntegerArray,其长度与从给定数组复制的所有元素相同。
        int[]arr2={1,2,3,4,5};
        AtomicIntegerArray array=new AtomicIntegerArray(arr2);
        //(2). 创建给定长度的新AtomicIntegerArray,所有元素最初为零。
        //AtomicIntegerArray array=new AtomicIntegerArray(5);

        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i]);
        }
        System.out.println();
        System.out.println("=======");
        array.getAndSet(0,1111);
        System.out.println("============");
        System.out.println("将数字中位置为0位置上的元素改为:"+array.get(0));
        System.out.println("数组位置为1位置上的旧值是:"+array.get(1));
        System.out.println("将数组位置为1位置上的数字进行加1的处理");
        array.getAndIncrement(1);
        System.out.println("数组位置为1位置上的新值是:"+array.get(1));
    }
}

④. 引用类型原子类 (AtomicReference、AtomicStampedReference、AtomicMarkableReference)

  • ①. 引用类型原子类主要有三个: AtomicReference、AtomicStampedReference、AtomicMark ableReference

  • ②. 使用AtomicReference来实现自旋锁案例

//自旋锁
public class AtomicReferenceThreadDemo {
    static AtomicReference<Thread>atomicReference=new AtomicReference<>();
    static Thread thread;
    public static void lock(){
        thread=Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"\t"+"coming.....");
        while(!atomicReference.compareAndSet(null,thread)){

        }
    }
    public static void unlock(){
        System.out.println(Thread.currentThread().getName()+"\t"+"over.....");
        atomicReference.compareAndSet(thread,null);
    }
    public static void main(String[] args) {
        new Thread(()->{
            AtomicReferenceThreadDemo.lock();
            try { TimeUnit.SECONDS.sleep(3);  } catch (InterruptedException e) {e.printStackTrace();}
            AtomicReferenceThreadDemo.unlock();
        },"A").start();

        new Thread(()->{
            AtomicReferenceThreadDemo.lock();
            AtomicReferenceThreadDemo.unlock();
        },"B").start();
    }
}
  • ③. AtomicStampedReference 解决ABA问题
  1. 携带版本号的引用类型原子类,可以解决ABA问题
  2. 解决修改过几次
  3. 状态戳原子引用
/**
 * Description: ABA问题的解决
 *
 * @author TANGZHI
 * @date 2021-03-26 21:30
 **/
public class ABADemo {
    private static AtomicReference<Integer> atomicReference=new AtomicReference<>(100);
    private static AtomicStampedReference<Integer> stampedReference=new AtomicStampedReference<>(100,1);
    public static void main(String[] args) {
        System.out.println("===以下是ABA问题的产生===");
        new Thread(()->{
            atomicReference.compareAndSet(100,101);
            atomicReference.compareAndSet(101,100);
        },"t1").start();

        new Thread(()->{
            //先暂停1秒 保证完成ABA
            try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println(atomicReference.compareAndSet(100, 2019)+"\t"+atomicReference.get());
        },"t2").start();
        try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println("===以下是ABA问题的解决===");

        new Thread(()->{
            int stamp = stampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+"\t 第1次版本号"+stamp+"\t值是"+stampedReference.getReference());
            //暂停1秒钟t3线程
            try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }

            stampedReference.compareAndSet(100,101,stampedReference.getStamp(),stampedReference.getStamp()+1);
            System.out.println(Thread.currentThread().getName()+"\t 第2次版本号"+stampedReference.getStamp()+"\t值是"+stampedReference.getReference());
            stampedReference.compareAndSet(101,100,stampedReference.getStamp(),stampedReference.getStamp()+1);
            System.out.println(Thread.currentThread().getName()+"\t 第3次版本号"+stampedReference.getStamp()+"\t值是"+stampedReference.getReference());
        },"t3").start();

        new Thread(()->{
            int stamp = stampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+"\t 第1次版本号"+stamp+"\t值是"+stampedReference.getReference());
            //保证线程3完成1次ABA
            try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
            boolean result = stampedReference.compareAndSet(100, 2019, stamp, stamp + 1);
            System.out.println(Thread.currentThread().getName()+"\t 修改成功否"+result+"\t最新版本号"+stampedReference.getStamp());
            System.out.println("最新的值\t"+stampedReference.getReference());
        },"t4").start();
    }
  • ④. AtomicMarkableReference 不建议用它解决ABA问题
  1. 原子更新带有标志位的引用类型对象
  2. 解决是否修改(它的定义就是将状态戳简化位true|false),类似一次性筷子
  3. 状态戳(true/false)原子引用
  4. 不建议用它解决ABA问题
public class ABADemo{
    static AtomicMarkableReference<Integer> markableReference = new AtomicMarkableReference<>(100,false);

    public static void main(String[] args){
        System.out.println("============AtomicMarkableReference不关心引用变量更改过几次,只关心是否更改过======================");
        new Thread(() -> {
            boolean marked = markableReference.isMarked();
            System.out.println(Thread.currentThread().getName()+"\t 1次版本号"+marked);
            try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }
            markableReference.compareAndSet(100,101,marked,!marked);
            System.out.println(Thread.currentThread().getName()+"\t 2次版本号"+markableReference.isMarked());
            markableReference.compareAndSet(101,100,markableReference.isMarked(),!markableReference.isMarked());
            System.out.println(Thread.currentThread().getName()+"\t 3次版本号"+markableReference.isMarked());
        },"线程A").start();

        new Thread(() -> {
            boolean marked = markableReference.isMarked();
            System.out.println(Thread.currentThread().getName()+"\t 1次版本号"+marked);
            //暂停几秒钟线程
            try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }
            markableReference.compareAndSet(100,2020,marked,!marked);
            System.out.println(Thread.currentThread().getName()+"\t"+markableReference.getReference()+"\t"+markableReference.isMarked());
        },"线程B").start();
    }
}
  • ⑤. AtomicStampedReference和AtomicMarkableReference区别
  1. stamped – version number 版本号,修改一次+1
  2. Markable – true、false 是否修改过

⑤. 对象的属性修改原子类 (AtomicIntegerFieldUp dater、AtomicLongFieldUpdater、AtomicRefere nceFieldUpdater)

  • ①. 使用目的:以一种线程安全的方式操作非线程安全对象内的某些字段
    (是否可以不要锁定整个对象,减少锁定的范围,只关注长期、敏感性变化的某一个字段,而不是整个对象,已达到精确加锁+节约内存的目的)

  • ②. 使用要求

  1. 更新的对象属性必须使用public volatile修饰符
  2. 因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须使用静态方法newUpdater( )创建一个更新器,并且需要设置想要更新的类和属性

Juc15_基本AtomicInteger、数组、引用AtomicStampedReference、对象的属性修改原子类AtomicIntegerFieldUp 、原子操作增强类LongAdder

  • ③. 你在哪里用到了volatile
  1. 单例设置模式(双端检锁机制)
  2. AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
  • ④. AtomicIntegerFieldUpdater:原子更新对象中int类型字段的值
/***
 1.从AtomicIntegerFieldUpdaterDemo代码中我们不难发现,通过AtomicIntegerFieldUpdater更新score我们获取最后的int值时相较于AtomicInteger来说不需要调用get()方法!
 2.对于AtomicIntegerFieldUpdaterDemo类的AtomicIntegerFieldUpdater是static final类型也就是说即使创建了100个对象AtomicIntegerField也只存在一个不会占用对象的内存,但是AtomicInteger会创建多个AtomicInteger对象,占用的内存比AtomicIntegerFieldUpdater大,
 所以对于熟悉dubbo源码的人都知道,dubbo有个实现轮询负载均衡策略的类AtomicPositiveInteger用的就是AtomicIntegerFieldUpdater。
 */
@SuppressWarnings("all")
public class AtomicIntegerFieldUpdaterDemo {
    private static final int THREAD_NUM = 1000;

    //设置栅栏是为了防止循环还没结束就执行main线程输出自增的变量,导致误以为线程不安全
    private static CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUM);
    Score score=new Score();
    public static void main(String[] args)throws InterruptedException {
        Score score = new Score();
        for (int j = 0; j < THREAD_NUM; j++) {
            new Thread(() -> {
                score.addTotalScore(score);
                countDownLatch.countDown();
            }).start();
        }
        countDownLatch.await();
        System.out.println("totalScore的值:" + score.totalScore);
    }
}

class Score {
    String username;

    public volatile int totalScore = 0;
    //public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass,String fieldName)
    private static AtomicIntegerFieldUpdater atomicIntegerFieldUpdater =
            AtomicIntegerFieldUpdater.newUpdater(Score.class, "totalScore");

    public void addTotalScore(Score score){
        //public int incrementAndGet(T obj) {
        atomicIntegerFieldUpdater.incrementAndGet(score);
    }
}
  • ⑤. AtomicReferenceFieldUpdater:原子更新引用类型字段的值
public class AtomicReferenceFieldUpdaterDemo {
    public static void main(String[] args) {
        MyCar myCar=new MyCar();
        AtomicReferenceFieldUpdater<MyCar,Boolean>atomicReferenceFieldUpdater=
                AtomicReferenceFieldUpdater.newUpdater(MyCar.class,Boolean.class,"flag");
        for (int i = 1; i <= 5; i++) {
            new Thread(()->{
                if(atomicReferenceFieldUpdater.compareAndSet(myCar,Boolean.FALSE,Boolean.TRUE)){
                    System.out.println(Thread.currentThread().getName()+"\t"+"---init.....");
                    try { TimeUnit.SECONDS.sleep(2);  } catch (InterruptedException e) {e.printStackTrace();}
                    System.out.println(Thread.currentThread().getName()+"\t"+"---init.....over");
                }else{
                    System.out.println(Thread.currentThread().getName()+"\t"+"------其它线程正在初始化");
                }
            },String.valueOf(i)).start();
        }

    }
}
class MyCar{
    public volatile Boolean flag=false;
}
  • ⑥. AtomicIntegerFieldUpdater与AtomicInteger使用引发的思考
  1. 通过下面代码我们不难得知使用AtomicIntegerFieldUpdater与AtomicInteger其实效果是一致的,那既然已经存在了AtomicInteger并发之神又要写一个AtomicIntegerFieldUpdater呢?
  2. 从AtomicIntegerFieldUpdaterDemo代码中我们不难发现,通过AtomicIntegerFieldUpdater更新score我们获取最后的int值时相较于AtomicInteger来说不需要调用get()方法!
  3. 对于AtomicIntegerFieldUpdaterDemo类的AtomicIntegerFieldUpdater是static final类型也就是说即使创建了100个对象AtomicIntegerField也只存在一个不会占用对象的内存,但是AtomicInt eger会创建多个AtomicInteger对象,占用的内存比AtomicIntegerFieldUpdater大,所以对于熟悉dubbo源码的人都知道,dubbo有个实现轮询负载均衡策略的类AtomicPositiveInteger用的就是AtomicIntegerField Update,在netty底层大量使用了这个类
 
    public static class Candidate {
        int id;
 
        volatile int score = 0;
 
        AtomicInteger score2 = new AtomicInteger();
    }
 
    public static final AtomicIntegerFieldUpdater<Candidate> scoreUpdater =
            AtomicIntegerFieldUpdater.newUpdater(Candidate.class, "score");
 
    public static AtomicInteger realScore = new AtomicInteger(0);
 
    public static void main(String[] args) throws InterruptedException {
        final Candidate candidate = new Candidate();
        Thread[] t = new Thread[10000];
        for (int i = 0; i < 10000; i++) {
            t[i] = new Thread() {
                @Override
                public void run() {
                    if (Math.random() > 0.4) {
                        candidate.score2.incrementAndGet();
                        scoreUpdater.incrementAndGet(candidate);
                        realScore.incrementAndGet();
                    }
                }
            };
            t[i].start();
        }
        for (int i = 0; i < 10000; i++) {
            t[i].join();
        }
        System.out.println("AtomicIntegerFieldUpdater Score=" + candidate.score);
        System.out.println("AtomicInteger Score=" + candidate.score2.get());
        System.out.println("realScore=" + realScore.get());
 
    }
}
/**
    AtomicIntegerFieldUpdater Score=5897
    AtomicInteger Score=5897
    realScore=5897
*/

⑥. 原子操作增强类(DoubleAccumulator 、DoubleAdder 、LongAccumulator 、LongAdder)

  • ①. 本小节主要讲:DoubleAccumulator 、DoubleAdder、LongAccumulator 、LongAdder

  • ②. 常用API

  1. void add(long x) :讲当前的value加x
  2. void increment( ) : 将当前的value加1
  3. void decrement( ) : 将当前value减1
  4. long sum( )
    返回当前的值,特别注意,在没有并发更新value的情况下,sum会返回一个精确值,在存在并发的情况下,sum不保证返回精确值
  5. long longvale–>等价于long sum( )
    将value重置为0,可用于替换重新new一个LongAdder,但次方法只可以在没有并发更新的情况下使用
  6. long sumThenReset( ) : 获取当前value,并将value重置为0
  • ③. 入门讲解(LongAdder|LongAccumulator区别)
  1. LongAdder只能用来计算加法、减法,且从零开始计算
  2. LongAccumulator提供了自定义的函数操作
public class LongAdderDemo {
    public static void main(String[] args) {
        // LongAdder只能做加减法,不能做乘除法
        LongAdder longAdder=new LongAdder();
        longAdder.increment();
        longAdder.increment();
        longAdder.increment();
        longAdder.decrement();

        System.out.println(longAdder.longValue());
        System.out.println("========");
        //LongAccumulator​(LongBinaryOperator accumulatorFunction, long identity)
        //LongAccumulator longAccumulator=new LongAccumulator((x,y)->x+y,0);
        LongAccumulator longAccumulator=new LongAccumulator(new LongBinaryOperator() {
            @Override
            public long applyAsLong(long left, long right) {
                return left*right;
            }
        },5);
        longAccumulator.accumulate(1);
        longAccumulator.accumulate(2);
        longAccumulator.accumulate(3);
        System.out.println(longAccumulator.longValue());
    }
}
  • ④. LongAdder高性能对比code演示
class  ClickNumber{
    int number=0;

    //(1). 使用synchronized实现number++
    public synchronized void add_synchronized(){
        number++;
    }
    //(2). 使用AtomicInteger
    AtomicInteger atomicInteger=new AtomicInteger();
    public void add_atomicInteger(){
        atomicInteger.incrementAndGet();
    }
   //(3). 使用AtomicLong
   AtomicLong atomicLong=new AtomicLong();
   public void add_atomicLong(){
       atomicLong.incrementAndGet();
   }
   //(4). 使用LongAdder
   LongAdder adder=new LongAdder();
   public void add_longAdder(){
       adder.increment();
   }
   //(5). 使用LongAccumulater
   LongAccumulator accumulator=new LongAccumulator((x,y)->x+y,0);
   public void add_longAccumulater(){
       accumulator.accumulate(1);
   }
}
/**
 * 50个线程,每个线程100w次,总点赞数出来
 * */
public class LongAdderCalcDemo {
    // 50个线程和每个线程点在100w次
    public static  final int SIZE_THREAD=50;
    public static  final int _1w=10000;
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch=new CountDownLatch(SIZE_THREAD);
        ClickNumber clickNumber=new ClickNumber();
        long startTime = System.currentTimeMillis();
        for (int i = 1 ; i <=SIZE_THREAD ; i++) {
            new Thread(()->{
                try{
                    for (int j = 1; j <=10*_1w; j++) {
                        //我们明显可以看到调用LongAdder性能最好
                        //clickNumber.add_synchronized();
                        clickNumber.add_longAdder();
                    }
                }finally {
                    countDownLatch.countDown();
                }
            },String.valueOf(i)).start();
        }
        countDownLatch.await();
        long endTime = System.currentTimeMillis();
        System.out.println("-----consTime:"+(endTime-startTime)+"毫秒"+"\t");
        System.out.println(clickNumber.adder.longValue());

    }
}
  • ⑤. AtomicLong和LongAdder的分别
    Juc15_基本AtomicInteger、数组、引用AtomicStampedReference、对象的属性修改原子类AtomicIntegerFieldUp 、原子操作增强类LongAdder
上一篇:angular源码分析:injector.js文件分析——angular中的依赖注入式如何实现的(续)


下一篇:JUC(5)原子类