java JUC并发编程 第七章 原子操作类增强

系列文章目录

第一章 java JUC并发编程 Future: link
第二章 java JUC并发编程 多线程锁: link
第三章 java JUC并发编程 中断机制: link
第四章 java JUC并发编程 java内存模型JMM: link
第五章 java JUC并发编程 volatile与JMM: link
第六章 java JUC并发编程 CAS: link
第七章 java JUC并发编程 原子操作类增强: link


文章目录

  • 系列文章目录
  • 1 概述
  • 2 分类说明
    • 2.1 基本类型原子类
      • 2.1.1 常用API简介
      • 2.1.2 case(不推荐)
      • 2.1.3 CountDownLatch优化上面case
    • 2.2 数组类型原子类
      • 2.2.1 case
    • 2.3 引用类型原子类
      • 2.3.1 AtomicReference,(前面的自旋锁Demo)
      • 2.3.2 AtomicStampedReference(前面的ABADemo)
      • 2.3.3 AtomicMarkableReference
    • 2.4 对象的属性修改原子类
      • 2.4.1 AtomicIntegerFieldUpdaterDemo(针对字段是int类型的)
      • 2.4.2 AtomicReferenceFieldUpdater(其他类型的字段)
    • 2.5 原子操作增强类原理深度解析
      • 2.5.1 点赞计数器
      • 2.5.2 性能评估
      • 2.5.3 原理、源码分析
        • 2.5.3.1 Striped64
        • 2.5.3.2 Striped64中一些变量或者方法的定义
        • 2.5.3.3 Cell单元格类
        • 2.5.3.4 LongAdder为什么这么快
        • 2.5.3.5 源码分析
          • 2.5.3.5.1 longAdder.increment()
          • 2.5.3.5.2 longAccumulate
          • 2.5.3.5.3 线程hash值:probe
          • 2.5.3.5.4 总纲
          • 2.5.3.5.5 计算
  • 3 总结


1 概述

java.util.concurrent.atomic 下的类
https://www.runoob.com/manual/jdk11api/java.base/java/util/concurrent/atomic/package-summary.html
java JUC并发编程 第七章 原子操作类增强_第1张图片

2 分类说明

2.1 基本类型原子类

AtomicInteger,AtomicBoolean,AtomicLong

2.1.1 常用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)//获取当前的值,并加上预期的值
boolean compareAndSet(int expect,int update)//如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update)

2.1.2 case(不推荐)

package com.atguigu.springcloud.util.interrup;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

class MyNumber2{
    AtomicInteger atomicInteger = new AtomicInteger();
    public void addPlusPlus(){
        atomicInteger.getAndIncrement();
    }
}

public class AtomicIntegerDemo {
    public static final int SIZE=50;

    public static void main(String[] args) throws InterruptedException{
        MyNumber2 myNumber2 = new MyNumber2();
        for (int i = 0; i < SIZE; i++) {
            new Thread(()->{
                for (int j = 1; j <= 1000; j++) {
                    myNumber2.addPlusPlus();
                }
            },String.valueOf(i)).start();
        }
        //等待50个线程计算完成后再获得最终值,否则在上面正在计算的时候main线程就去看结果,造成看到的结果不太准确。但是实际中不能这么写
        try {TimeUnit.MILLISECONDS.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}
        System.out.println(Thread.currentThread().getName()+"\t"+"result:"+myNumber2.atomicInteger.get());
    }
}

2.1.3 CountDownLatch优化上面case

package com.atguigu.springcloud.util.interrup;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

class MyNumber2{
    AtomicInteger atomicInteger = new AtomicInteger();
    public void addPlusPlus(){
        atomicInteger.getAndIncrement();
    }
}

public class AtomicIntegerDemo {
    public static final int SIZE=50;

    public static void main(String[] args) throws InterruptedException{
        MyNumber2 myNumber2 = new MyNumber2();
        CountDownLatch countDownLatch = new CountDownLatch(SIZE);
        for (int i = 0; i < SIZE; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <= 1000; j++) {
                        myNumber2.addPlusPlus();
                    }
                } finally {
                    //完成一个减一个
                    countDownLatch.countDown();
                }
            },String.valueOf(i)).start();
        }
        //等待50个线程计算完成后再获得最终值,否则在上面正在计算的时候main线程就去看结果,造成看到的结果不太准确。但是实际中不能这么写
//        try {TimeUnit.MILLISECONDS.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}

        //组织main线程获取数据,必须等到CountDownLatch的50个线程跑完
        countDownLatch.await();
        System.out.println(Thread.currentThread().getName()+"\t"+"result:"+myNumber2.atomicInteger.get());
    }
}

2.2 数组类型原子类

AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray

2.2.1 case

package com.atguigu.springcloud.util.interrup;

import java.util.concurrent.atomic.AtomicIntegerArray;

public class AtomicIntegerArrayDemo {
    public static void main(String[] args) {
        AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[5]);
//        AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(5);
//        AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[]{1,2,3,4,5});
        for (int i = 0; i < atomicIntegerArray.length(); i++) {
            System.out.println(atomicIntegerArray.get(i));
        }
        System.out.println();
        
        int tmpInt = 0;
        tmpInt = atomicIntegerArray.getAndSet(0,1122);
        System.out.println(tmpInt+"\t"+atomicIntegerArray.get(0));

        tmpInt = atomicIntegerArray.getAndIncrement(0);
        System.out.println(tmpInt+"\t"+atomicIntegerArray.get(0));

    }
}

2.3 引用类型原子类

2.3.1 AtomicReference,(前面的自旋锁Demo)

2.3.2 AtomicStampedReference(前面的ABADemo)

携带版本号的引用类型原子类,可以解决ABA问题
解决多次的问题

2.3.3 AtomicMarkableReference

原子更新是否带有标记为的引用类型对象
解决一次性的问题,是否修改过。它的定义就是将状态戳简化为true|false

package com.atguigu.springcloud.util.interrup;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicMarkableReference;

public class AtomicMarkableReferenceDemo {
    static AtomicMarkableReference markableReference = new AtomicMarkableReference(100,false);

    public static void main(String[] args) {
        new Thread(()->{
            boolean marked = markableReference.isMarked();
            System.out.println(Thread.currentThread().getName()+"\t"+"默认标识"+"marked");
            //等待t2线程和t1拿到一样的标识,都是false
            try {TimeUnit.MILLISECONDS.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
            markableReference.compareAndSet(100,1000,marked,!marked);
        },"t1").start();

        new Thread(()->{
            boolean marked = markableReference.isMarked();
            System.out.println(Thread.currentThread().getName()+"\t"+"默认标识"+"marked");
            try {TimeUnit.MILLISECONDS.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}
            boolean b = markableReference.compareAndSet(100, 2000, marked, !marked);
            System.out.println(Thread.currentThread().getName()+"\t"+"t2线程CASresult:"+b);
            System.out.println(Thread.currentThread().getName()+"\t"+markableReference.isMarked());
            System.out.println(Thread.currentThread().getName()+"\t"+markableReference.getReference());
        },"t2").start();
    }
}

2.4 对象的属性修改原子类

AtomicLntegerFieldUpdater:原子更新对象中int类型字段的值
AtomicLongFieldUpdater:原子更新对象中Long类型字段的值
AtomicReferenceFieldUpdater:原子更新引用类型字段的值
使用目的:以一种线程安全的方式操作非线程安全对象的某些字段
使用要求:
1.更新的对象属性必须使用public volatile修饰符
2.因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性
面试问题:你在哪里用了volatile?:AtomicReferenceFieldUpdater

2.4.1 AtomicIntegerFieldUpdaterDemo(针对字段是int类型的)

package com.atguigu.springcloud.util.interrup;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

class BankAccount{
    String bankName="ccb";
    //更新的对象属性必须使用public volatile修饰符
    public volatile int money=0;//钱数
    public synchronized void add(){
        money++;
    }
    //因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须
    //使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性
    AtomicIntegerFieldUpdater<BankAccount> fieldUpdater =AtomicIntegerFieldUpdater.newUpdater(BankAccount.class,"money");
    //不加synchronized,保证高性能原子性,只影响到变量
    public void transMoney(BankAccount bankAccount){
        fieldUpdater.getAndIncrement(bankAccount);
    }
}

/**
 * 以一种线程安全的方式操作非线程安全对象的某些字段
 * 需求:
 * 10个线程
 * 每个线程转账1000
 * 不使用synchronized,尝试使用AtomicIntegerFieldUpdater来实现
 */
public class AtomicIntegerFieldUpdaterDemo {
    public static void main(String[] args) throws InterruptedException {
        BankAccount bankAccount = new BankAccount();
        CountDownLatch countDownLatch = new CountDownLatch(10);
        for (int i = 1; i <= 10; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <=1000 ; j++) {
//                        bankAccount.add();
                        bankAccount.transMoney(bankAccount);
                    }
                } finally {
                    countDownLatch.countDown();

                }
            },String.valueOf(i)).start();
        }
        countDownLatch.await();
        System.out.println(Thread.currentThread().getName()+"\t"+"result:"+bankAccount.money);
    }
}

2.4.2 AtomicReferenceFieldUpdater(其他类型的字段)

package com.atguigu.springcloud.util.interrup;


import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

class MyVar{
    public volatile Boolean isInit = Boolean.FALSE;
    AtomicReferenceFieldUpdater<MyVar,Boolean> referenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(MyVar.class,Boolean.class,"isInit");
    public void init(MyVar myVar){
        if(referenceFieldUpdater.compareAndSet(myVar,Boolean.FALSE,Boolean.TRUE)){
            System.out.println(Thread.currentThread().getName()+"\t"+"----start init,need 3seconds");
            try {TimeUnit.MILLISECONDS.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}
            System.out.println(Thread.currentThread().getName()+"\t"+"----over init");
        }else{
            System.out.println(Thread.currentThread().getName()+"\t"+"----已经有线程在进行初始化工作");
        }

    }
}
/**
 * 需求:
 * 多线程并发调用一个类的初始化方法,如果未被初始化过,将执行初始化工作,
 * 要求只能被初始化一次,只有一个线程操作成功
 */
public class AtomicReferenceFieldUpdaterDemo {
    public static void main(String[] args) {
        MyVar myVar = new MyVar();
        for (int i = 1; i <=5 ; i++) {
            new Thread(()->{
                myVar.init(myVar);
            },String.valueOf(i)).start();
        }
    }
}

2.5 原子操作增强类原理深度解析

DoubleAccumulator,DoubleAdder,LongAccumulator,LongAdder
java JUC并发编程 第七章 原子操作类增强_第2张图片

2.5.1 点赞计数器

常用API
java JUC并发编程 第七章 原子操作类增强_第3张图片
LongAdder只能用来计算加法,并且从零开始计算
LongAccumulator提供了自定义的函数操作
LongAdderAPIDemo

package com.atguigu.springcloud.util.interrup;

import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.LongBinaryOperator;

public class LongAdderAPIDEmo {
    public static void main(String[] args) {

        LongAdder longAdder = new LongAdder();
        longAdder.increment();
        longAdder.increment();
        longAdder.increment();
        System.out.println(longAdder.sum());


/*        LongAccumulator longAccumulator = new LongAccumulator(new LongBinaryOperator() {
            @Override
            public long applyAsLong(long left, long right) {
                return left+right;
            }
        },0);//从0开始与下面的写法功能一样*/
        LongAccumulator longAccumulator = new LongAccumulator((x,y)->x+y,0);
        longAccumulator.accumulate(1);//1
        longAccumulator.accumulate(3);//4
        System.out.println(longAccumulator.get());
    }
}

2.5.2 性能评估

package com.atguigu.springcloud.util.interrup;

import lombok.SneakyThrows;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;

class ClickNumber{
    int number = 0;
    //第一种传统 synchronized
    public synchronized  void clickBySynchronized(){
        number++;
    }
    //第二种 AtomicLong
    AtomicLong atomicLong = new AtomicLong(0);
    public void clickByAtomicLong(){
        atomicLong.getAndIncrement();
    }
    //第三种 LongAdder
    LongAdder longAdder = new LongAdder();
    public void clickByLongAdder(){
        longAdder.increment();
    }
    //第四种 LongAccumulator
    LongAccumulator longAccumulator = new LongAccumulator((x,y)->x+y,0);
    public void clickByLongAccumulator(){
        longAccumulator.accumulate(1);
    }
}
/**
 * 需求:50个线程,每个线程100w次,总点赞数出来
 */
public class AccumulatorCompareDemo {
    public static final int _1w = 10000;
    public static final int threadNumber=50;

    @SneakyThrows
    public static void main(String[] args) {
        ClickNumber clickNumber = new ClickNumber();
        long startTime;
        long endTime;

        CountDownLatch countDownLatch1 = new CountDownLatch(threadNumber);
        CountDownLatch countDownLatch2 = new CountDownLatch(threadNumber);
        CountDownLatch countDownLatch3 = new CountDownLatch(threadNumber);
        CountDownLatch countDownLatch4 = new CountDownLatch(threadNumber);

        startTime = System.currentTimeMillis();
        for (int i = 1; i <=threadNumber ; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <=100*_1w ; j++) {
                        clickNumber.clickBySynchronized();
                    }
                } finally {
                    countDownLatch1.countDown();
                }
            },String.valueOf(i)).start();
        }
        countDownLatch1.await();
        endTime = System.currentTimeMillis();
        System.out.println("----costTime:"+(endTime-startTime)+"\t"+"毫秒clickBySynchronized"+clickNumber.number);


        startTime = System.currentTimeMillis();
        for (int i = 1; i <=threadNumber ; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <=100*_1w ; j++) {
                        clickNumber.clickByAtomicLong();
                    }
                } finally {
                    countDownLatch2.countDown();
                }
            },String.valueOf(i)).start();
        }
        countDownLatch2.await();
        endTime = System.currentTimeMillis();
        System.out.println("----costTime:"+(endTime-startTime)+"\t"+"毫秒clickByAtomicLong"+clickNumber.atomicLong.get());

        startTime = System.currentTimeMillis();
        for (int i = 1; i <=threadNumber ; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <=100*_1w ; j++) {
                        clickNumber.clickByLongAdder();
                    }
                } finally {
                    countDownLatch3.countDown();
                }
            },String.valueOf(i)).start();
        }
        countDownLatch3.await();
        endTime = System.currentTimeMillis();
        System.out.println("----costTime:"+(endTime-startTime)+"\t"+"clickByLongAdder"+clickNumber.longAdder.sum());

        startTime = System.currentTimeMillis();
        for (int i = 1; i <=threadNumber ; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <=100*_1w ; j++) {
                        clickNumber.clickByLongAccumulator();
                    }
                } finally {
                    countDownLatch4.countDown();
                }
            },String.valueOf(i)).start();
        }
        countDownLatch4.await();
        endTime = System.currentTimeMillis();
        System.out.println("----costTime:"+(endTime-startTime)+"\t"+"clickByLongAccumulator"+clickNumber.longAccumulator.get());
    }
}

java JUC并发编程 第七章 原子操作类增强_第4张图片

2.5.3 原理、源码分析

java JUC并发编程 第七章 原子操作类增强_第5张图片
java JUC并发编程 第七章 原子操作类增强_第6张图片

2.5.3.1 Striped64

Striped64有几个比较重要的成员函数
java JUC并发编程 第七章 原子操作类增强_第7张图片

2.5.3.2 Striped64中一些变量或者方法的定义

2.5.3.3 Cell单元格类

是java.util.concurrent.atomic 下Striped64的一个内部类
java JUC并发编程 第七章 原子操作类增强_第8张图片

2.5.3.4 LongAdder为什么这么快

在这里插入图片描述
java JUC并发编程 第七章 原子操作类增强_第9张图片
java JUC并发编程 第七章 原子操作类增强_第10张图片

2.5.3.5 源码分析

在这里插入图片描述
java JUC并发编程 第七章 原子操作类增强_第11张图片

2.5.3.5.1 longAdder.increment()

add(L)
当线程变多的情况下第二个红框 !casBase(b = base, b+x)的cas操作就有可能没有抢到而返回了false,!false的情况下就进入了if逻辑块中,默认情况下uncontended变量为true(没有冲突)会进入longAccumulate(x,null,uncontended)方法。
278:新建Cells数组模式2大小
红色框代码(a=as[getProbe() & m])判断槽位是否有值
最后一个红框cas操作失败的情况了2槽位也顶不住就会继续执行longAccumulate扩容
java JUC并发编程 第七章 原子操作类增强_第12张图片

java JUC并发编程 第七章 原子操作类增强_第13张图片

java JUC并发编程 第七章 原子操作类增强_第14张图片

java JUC并发编程 第七章 原子操作类增强_第15张图片

java JUC并发编程 第七章 原子操作类增强_第16张图片

2.5.3.5.2 longAccumulate

java JUC并发编程 第七章 原子操作类增强_第17张图片

2.5.3.5.3 线程hash值:probe

java JUC并发编程 第七章 原子操作类增强_第18张图片

java JUC并发编程 第七章 原子操作类增强_第19张图片

2.5.3.5.4 总纲

java JUC并发编程 第七章 原子操作类增强_第20张图片
阅读源码顺序简易2,3,1.
在这里插入图片描述

2.5.3.5.5 计算

CASE2

CASE3
java JUC并发编程 第七章 原子操作类增强_第21张图片
CASE1
第一个if
java JUC并发编程 第七章 原子操作类增强_第22张图片
第二个if
java JUC并发编程 第七章 原子操作类增强_第23张图片

第三个if
java JUC并发编程 第七章 原子操作类增强_第24张图片

第四个if
java JUC并发编程 第七章 原子操作类增强_第25张图片

第五个if
java JUC并发编程 第七章 原子操作类增强_第26张图片

第六个if
java JUC并发编程 第七章 原子操作类增强_第27张图片

在这里插入图片描述
sum
sum()会将所有Cell数组中的value和base累加作为返回值。
核心的思想就是将之前AtomicLong一个value的更新压力分散到多个value中取,从而降低更新热点
java JUC并发编程 第七章 原子操作类增强_第28张图片
java JUC并发编程 第七章 原子操作类增强_第29张图片

3 总结

AtomicLong
线程安全,可允许一些性能损耗,要求高精度可使用
保证精度,性能代价
AtomicLong是多个线程针对单个热点值Value进行原子操作
LongAdder
当需要在高并发下有较好的性能表现,且对值得精确度要求不高时,可以使用
保证性能,精度代价
LongAdder是每个线程拥有自己的槽,各个线程一般只对自己槽中的那个值进行CAS操作

你可能感兴趣的:(java,开发语言)