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
第八章 java JUC并发编程 ThreadLocal: link
第九章 java JUC并发编程 对象内存布局与对象头: link
第十章 java JUC并发编程 Synchronized与锁升级: link
第十一章 java JUC并发编程 AbstractQueuedSynchronizer之AQS: link



1 概述

java.util.concurrent.atomic 下的类
https://www.runoob.com/manual/jdk11api/java.base/java/util/concurrent/atomic/package-summary.html
在这里插入图片描述

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
在这里插入图片描述

2.5.1 点赞计数器

常用API
在这里插入图片描述
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());
    }
}

在这里插入图片描述

2.5.3 原理、源码分析

在这里插入图片描述
在这里插入图片描述

2.5.3.1 Striped64

Striped64有几个比较重要的成员函数
在这里插入图片描述

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

在这里插入图片描述

2.5.3.3 Cell单元格类

是java.util.concurrent.atomic 下Striped64的一个内部类
在这里插入图片描述

2.5.3.4 LongAdder为什么这么快

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.5.3.5 源码分析

在这里插入图片描述
在这里插入图片描述

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扩容
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.5.3.5.2 longAccumulate

在这里插入图片描述

在这里插入图片描述

2.5.3.5.3 线程hash值:probe

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.5.3.5.4 总纲

在这里插入图片描述
阅读源码顺序简易2,3,1.
在这里插入图片描述

2.5.3.5.5 计算

CASE2
在这里插入图片描述
CASE3
在这里插入图片描述
CASE1
第一个if
在这里插入图片描述
第二个if
在这里插入图片描述

第三个if
在这里插入图片描述

第四个if
在这里插入图片描述

第五个if
在这里插入图片描述

第六个if
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
sum
sum()会将所有Cell数组中的value和base累加作为返回值。
核心的思想就是将之前AtomicLong一个value的更新压力分散到多个value中取,从而降低更新热点
在这里插入图片描述
在这里插入图片描述

3 总结

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值