Java-Atomic原子操作类详解及源码分析,Java原子操作类进阶,LongAdder源码分析

文章目录

  • 一、Java原子操作类概述
    • 1、什么是原子操作类
    • 2、为什么要用原子操作类
    • 3、CAS入门
  • 二、基本类型原子类
    • 1、概述
    • 2、代码实例
  • 三、数组类型原子类
    • 1、概述
    • 2、代码实例
  • 四、引用类型原子类
    • 1、概述
    • 2、AtomicReference
    • 3、ABA问题与AtomicStampedReference
    • 4、一次性修改:AtomicMarkableReference
  • 五、对象属性修改原子类
    • 1、概述
    • 2、使用要求
    • 3、为什么要用对象属性修改原子类
    • 4、AtomicIntegerFieldUpdater使用实例
    • 5、AtomicReferenceFieldUpdater使用实例
    • 5、AtomicIntegerFieldUpdater与synchronized、AtomicInteger效率对比
  • 六、原子操作增强类
    • 1、概述
    • 2、LongAdder常用方法
    • 3、LongAdder使用实例
    • 4、LongAccumulator
    • 5、synchronized、AtomicLong、LongAdder、LongAccumulator性能对比
  • 七、LongAdder源码分析

一、Java原子操作类概述

1、什么是原子操作类

Java从JDK1.5开始提供了java.util.concurrent.atomic包,这个包中的原子操作类提供了一种用法简单,性能高效,线程安全地更新一个变量的方式。
在这里插入图片描述

2、为什么要用原子操作类

《阿里巴巴Java开发手册中》:

【参考】 volatile 解决多线程内存不可见问题。对于一写多读,是可以解决变量同步问题,但是如果多写,同样无法解决线程安全问题。
说明: 如果是 count++操作,使用如下类实现: AtomicInteger count = new AtomicInteger();
count.addAndGet(1); 如果是 JDK8,推荐使用 LongAdder 对象,比 AtomicLong 性能更好(减少乐观锁的重试次数)。

在 Java 语言中,我们可以通过 synchronized 关键字或者锁(Lock)来实现部分的原子性控制。然而,这种方式可能会造成性能上的问题,特别是在高并发的情况下。因此,Java 提供了一系列的原子类(如 AtomicInteger,AtomicLong 等),这些类使用了硬件级别的原子操作指令,能够在无锁的情况下实现高效的原子操作。

3、CAS入门

我知道乐观锁,但是我的确不知道CAS啊,到底什么是CAS

总的来说,原子操作类是基于CAS实现的,这些类在内部使用了非阻塞算法和硬件级别的 CAS 操作(Compare and Swap,比较并交换),保证了并发环境下的原子性和高性能。

二、基本类型原子类

1、概述

基本类型原子类包括AtomicBoolean、AtomicInteger、AtomicLong,

主要有以下几个常用方法:

// 自动更新当前值与给定的功能应用到当前和给定值的结果,返回更新后的值。 
int accumulateAndGet(int x, IntBinaryOperator accumulatorFunction) // 自动添加给定值并获取当前值。  
int addAndGet(int delta) // 自动设置的值来指定更新值,如果给定==期望值。  
boolean compareAndSet(int expect, int update) // 递减
int decrementAndGet() // 为扩大基本转换后的 double返回该 AtomicInteger值。  
double doubleValue() // 为扩大基本转换后的 float返回该 AtomicInteger值。  
float floatValue() // 获取当前值。  
int get() // 自动更新当前值与给定的功能应用到当前和给定值的结果,返回前一个值。  
int getAndAccumulate(int x, IntBinaryOperator accumulatorFunction) // 自动添加给定值并获取当前值。  
int getAndAdd(int delta) // 获取并递减
int getAndDecrement() // 获取并递增
int getAndIncrement() // 获取值并设置新值
int getAndSet(int newValue) // 通过函数式接口更新值
int getAndUpdate(IntUnaryOperator updateFunction) // 递增并且获取
int incrementAndGet() // 作为一个 int返回该 AtomicInteger值。  
int intValue() // 最终设置为给定的值。 
void lazySet(int newValue) // 为扩大基本转换后的 long返回该 AtomicInteger值。  
long longValue() // 设置值
void set(int newValue) // 返回当前值的字符串表示形式。  
String toString() // 自动更新当前值与结果应用给定的函数,返回更新后的值。  
int updateAndGet(IntUnaryOperator updateFunction) // 自动设置的值来指定更新值,如果修改值==期望值。 
boolean weakCompareAndSet(int expect, int update) 

2、代码实例

使用原子操作类,可以保证所有的操作都是原子操作,高并发下可以保证线程安全。

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;class MyNumber
{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{MyNumber myNumber = new MyNumber();CountDownLatch countDownLatch = new CountDownLatch(SIZE);for (int i = 1; i <=SIZE; i++) {new Thread(() -> {try {for (int j = 1; j <=1000; j++) {myNumber.addPlusPlus();}} finally {countDownLatch.countDown();}},String.valueOf(i)).start();}//等待上面50个线程全部计算完成后,再去获得最终值countDownLatch.await();System.out.println(Thread.currentThread().getName()+"\t"+"result: "+myNumber.atomicInteger.get());}
}

三、数组类型原子类

1、概述

数组类型原子类顾名思义,是基本类型与引用类型原子类的数组,包括:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray(引用类型的数组)。

主要常用方法:

// 使用将给定函数 更新i下标处的值为x ,并返回更新后的值
int accumulateAndGet(int i, int x, IntBinaryOperator accumulatorFunction) // 下标i 的值加delta
int addAndGet(int i, int delta) // 下标i 的值如果等于期望值,就设置为update,返回是否更新成功
boolean compareAndSet(int i, int expect, int update) // 下标i 递减并返回
int decrementAndGet(int i) // 获取下标i 的值
int get(int i) // 获取值,并通过给定函数设置下标i 的值
int getAndAccumulate(int i, int x, IntBinaryOperator accumulatorFunction) // 获取下标i的值,并加上给定的值
int getAndAdd(int i, int delta) // 获取下标i的值并减1
int getAndDecrement(int i) // 获取下标i的值,并加1
int getAndIncrement(int i) // 获取下标i的值,并设置为新值
int getAndSet(int i, int newValue) 
// 获取下标i的值,并通过函数设置新值 
int getAndUpdate(int i, IntUnaryOperator updateFunction) // 递增下标i的值并返回递增后的值 
int incrementAndGet(int i) 
// 懒加载
void lazySet(int i, int newValue) 
// 数组长度  
int length() 
// 设置下标i的值
void set(int i, int newValue) // 通过函数修改值,并且获取修改后的值
int updateAndGet(int i, IntUnaryOperator updateFunction) // 修改值
boolean weakCompareAndSet(int i, int expect, int update) 

我们可以发现,数组类型原子类与基本类型原子类的方法大同小异,无非是可以自由的操作数组中某个下标的值。

2、代码实例

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));}
}

四、引用类型原子类

1、概述

引用类型原子类,就是可以操作自定义类型的原子类,包括:AtomicReference、AtomicStampedReference、AtomicMarkableReference。

其主要方法基本也都相似,此处就不一一举例了。

2、AtomicReference

以下实例中,使用AtomicReference实现了对User类型的原子操作:

import java.util.concurrent.atomic.AtomicReference;
/**
* 原子引用
* 如果想包装某个类,就用原子引用
*/
public class AtomicReferenceDemo {public static void main(String[] args) {User u1 = new User("zhangsan", 14);User u2 = new User("lisi", 15);AtomicReference<User> atomicReference = new AtomicReference<>();atomicReference.set(u1);// true 设置为lisiSystem.out.println(atomicReference.compareAndSet(u1, u2) + "当前值" +atomicReference.get().getName());// false 还是lisiSystem.out.println(atomicReference.compareAndSet(u1, u2) + "当前值" +atomicReference.get().getName());}
}class User{private String name;private Integer age;public User(String name, Integer age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}
}

3、ABA问题与AtomicStampedReference

CAS会导致“ABA问题”。

CAS算法实现一个重要前提需要取出内存中某时刻的数据并在当下时刻比较并替换,那么在这个时间差类会导致数据的变化。

比如说一个线程1从内存位置V中取出A,这时候另一个线程2也从内存中取出A,并且线程2进行了一些操作将值变成了B,然后线程2又将值变成A。此时线程1进行CAS操作发现内存中仍然是A,然后线程1操作成功。

尽管线程1的CAS操作成功,但是不代表这个过程就是没有问题的。

中间过程如果不介意别人动过,那无所谓。

中间过程别人不能动,那就有问题了。

// ABA问题的产生与解决
public class ABADemo
{static AtomicInteger atomicInteger = new AtomicInteger(100);static AtomicStampedReference<Integer> stampedReference = new AtomicStampedReference<>(100,1);public static void main(String[] args){new Thread(() -> {int stamp = stampedReference.getStamp();System.out.println(Thread.currentThread().getName()+"\t"+"首次版本号:"+stamp);//暂停500毫秒,保证后面的t4线程初始化拿到的版本号和我一样try { TimeUnit.MILLISECONDS.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); }stampedReference.compareAndSet(100,101,stampedReference.getStamp(),stampedReference.getStamp()+1);System.out.println(Thread.currentThread().getName()+"\t"+"2次流水号:"+stampedReference.getStamp());stampedReference.compareAndSet(101,100,stampedReference.getStamp(),stampedReference.getStamp()+1);System.out.println(Thread.currentThread().getName()+"\t"+"3次流水号:"+stampedReference.getStamp());},"t3").start();new Thread(() -> {int stamp = stampedReference.getStamp();System.out.println(Thread.currentThread().getName()+"\t"+"首次版本号:"+stamp);//暂停1秒钟线程,等待上面的t3线程,发生了ABA问题try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }boolean b = stampedReference.compareAndSet(100, 2022, stamp, stamp + 1);System.out.println(b+"\t"+stampedReference.getReference()+"\t"+stampedReference.getStamp());},"t4").start();}// ABA问题的产生private static void abaHappen(){new Thread(() -> {atomicInteger.compareAndSet(100,101);try { TimeUnit.MILLISECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }atomicInteger.compareAndSet(101,100);},"t1").start();new Thread(() -> {try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }System.out.println(atomicInteger.compareAndSet(100, 2022)+"\t"+atomicInteger.get());},"t2").start();}
}

以上实例,我们使用AtomicStampedReference,对每次操作都使用一个版本号来解决,并且每次修改数据将版本号+1,可以解决ABA问题。

4、一次性修改:AtomicMarkableReference

AtomicMarkableReference维护一个对象引用和一个标记位(true、false),该标记位可以通过原子方式进行更新。

类似于将AtomicStampedReference时间戳状态简化为了true、false,常用于对象初始化工作。

isMarked()方法返回当前的标记值(true、false);
关键方法 compareAndSet(V expectedReference, V newReference, boolean expectedMark, boolean newMark),可以根据预期的标记值和新的标记值、预期的对象和新的对象,自由的设置值。

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);//暂停1秒钟线程,等待后面的T2线程和我拿到一样的模式flag标识,都是falsetry { TimeUnit.SECONDS.sleep(1); } 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.SECONDS.sleep(2); } 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();}
}

五、对象属性修改原子类

1、概述

对象属性修改原子类,就是用于直接修改对象的属性,以一种线程安全的方式操作非线程安全对象内的某些字段。

包含三个类:AtomicIntegerFieldUpdater(原子更新对象中int、Integer类型字段的值)、AtomicLongFieldUpdater(原子更新对象中Long、long类型字段的值)、AtomicReferenceFieldUpdater(原子更新引用类型字段的值)。

2、使用要求

更新的对象属性必须使用volatile修饰符,否则会报错:
在这里插入图片描述
因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性。

3、为什么要用对象属性修改原子类

比如以下实例,银行转账想要通过传统方式,只能使用synchronized或者Lock保证线程安全,而这种方式会使并发量急剧下降;又或者使用AtomicInteger原子类保证线程安全,但是该字段又无法通过JSON、MyBatis等数据库操作或者其他操作自动转换,此时就考虑使用AtomicIntegerFieldUpdater。

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;class BankAccount//资源类
{String bankName = "CCB";private volatile int money = 0;//钱数public void add(){money++;}public int getMoney() {return money;}
}/*** 以一种线程安全的方式操作非线程安全对象的某些字段。** 需求:* 10个线程,* 每个线程转账1000*/
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();}} finally {countDownLatch.countDown();}},String.valueOf(i)).start();}countDownLatch.await();System.out.println(Thread.currentThread().getName()+"\t"+"result: "+bankAccount.getMoney());}
}

4、AtomicIntegerFieldUpdater使用实例

AtomicIntegerFieldUpdater的方法与上述原子引用类相似,此处不再详细解释。

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;class BankAccount//资源类
{String bankName = "CCB";//更新的对象属性必须使用 volatile 修饰符。private volatile int money = 0;//钱数public void add(){money++;}public int getMoney() {return 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.getMoney());}
}

5、AtomicReferenceFieldUpdater使用实例

AtomicReferenceFieldUpdater与AtomicIntegerFieldUpdater使用类似,AtomicReferenceFieldUpdater用于更新更复杂的属性。

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 2 seconds");//暂停几秒钟线程try { TimeUnit.SECONDS.sleep(3); } 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();}}
}

5、AtomicIntegerFieldUpdater与synchronized、AtomicInteger效率对比

我们简单使用一个小例子,通过结果我们发现,AtomicIntegerFieldUpdater方式性能比AtomicInteger稍差一些,但是远优于synchronized:

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;public class Test {static class Bank {/*** 使用AtomicIntegerFieldUpdater*/public volatile int fieldUpdaterMoney = 0;AtomicIntegerFieldUpdater<Test.Bank> fieldUpdater =AtomicIntegerFieldUpdater.newUpdater(Test.Bank.class,"fieldUpdaterMoney");public void fieldUpdaterMoney(Test.Bank bankAccount){fieldUpdater.getAndIncrement(bankAccount);}/*** 使用synchronized*/public int synchronizedMoney = 0;public synchronized void synchronizedMoney(){synchronizedMoney++;}/*** 使用AtomicInteger*/AtomicInteger atomicIntegerMoney = new AtomicInteger(0);public void atomicIntegerMoney() {atomicIntegerMoney.getAndIncrement();}}public static final int threadNumber = 50;public static final int _1W = 10000;public static void main(String[] args) throws InterruptedException {long startTime;long endTime;CountDownLatch countDownLatch1 = new CountDownLatch(threadNumber);CountDownLatch countDownLatch2 = new CountDownLatch(threadNumber);CountDownLatch countDownLatch3 = new CountDownLatch(threadNumber);Bank bank = new Bank();startTime = System.currentTimeMillis();for (int i = 1; i <=threadNumber; i++) {new Thread(() -> {try {for (int j = 1; j <=100 * _1W; j++) {bank.synchronizedMoney();}} finally {countDownLatch1.countDown();}},String.valueOf(i)).start();}countDownLatch1.await();endTime = System.currentTimeMillis();System.out.println("----costTime: "+(endTime - startTime) +" 毫秒"+"\t synchronizedMoney: "+bank.synchronizedMoney);startTime = System.currentTimeMillis();for (int i = 1; i <=threadNumber; i++) {new Thread(() -> {try {for (int j = 1; j <=100 * _1W; j++) {bank.atomicIntegerMoney();}} finally {countDownLatch2.countDown();}},String.valueOf(i)).start();}countDownLatch2.await();endTime = System.currentTimeMillis();System.out.println("----costTime: "+(endTime - startTime) +" 毫秒"+"\t atomicIntegerMoney: "+bank.atomicIntegerMoney.get());startTime = System.currentTimeMillis();for (int i = 1; i <=threadNumber; i++) {new Thread(() -> {try {for (int j = 1; j <=100 * _1W; j++) {bank.fieldUpdaterMoney(bank);}} finally {countDownLatch3.countDown();}},String.valueOf(i)).start();}countDownLatch3.await();endTime = System.currentTimeMillis();System.out.println("----costTime: "+(endTime - startTime) +" 毫秒"+"\t fieldUpdaterMoney: "+bank.fieldUpdaterMoney);}
}

----costTime: 3513 毫秒 synchronizedMoney: 50000000
----costTime: 816 毫秒 atomicIntegerMoney: 50000000
----costTime: 1138 毫秒 fieldUpdaterMoney: 50000000

六、原子操作增强类

1、概述

原子操作增强类包含DoubleAccumulator、DoubleAdder、LongAccumulator、LongAdder。
LongAdder只能用于计算加法,且只能从0开始计算,LongAccumulator提供了自定义的函数操作。

《阿里巴巴Java开发手册中》:

【参考】 volatile 解决多线程内存不可见问题。对于一写多读,是可以解决变量同步问题,但是如果多写,同样无法解决线程安全问题。
说明: 如果是 count++操作,使用如下类实现: AtomicInteger count = new AtomicInteger();
count.addAndGet(1); 如果是 JDK8,推荐使用 LongAdder 对象,比 AtomicLong 性能更好(减少乐观锁的重试次数)。

在高并发下,推荐使用原子操作增强类,它提供了比原子操作类更好的性能。

2、LongAdder常用方法

LongAdder常用方法,DoubleAdder类似。

// 将当前的value加x
void add(long x)// 将当前的value加1
void increment()// 将当前value减1
void decrement()// 返回当前值。特别注意,在没有并发更新value的情况下,sum会返回一个精确值,在存在并发的情况下,sum不包装返回精确值
long sum()// 将value重置为0,可用于替代重新new一个LongAdder,但此方法只可以在没有并发更新的情况下使用。
void reset()// 获取当前value,并将value重置为0
long sumThenReset()

3、LongAdder使用实例

LongAdder longAdder = new LongAdder();
// 高并发下线程安全
longAdder.increment();
longAdder.increment();
longAdder.increment();System.out.println(longAdder.sum());

4、LongAccumulator

LongAdder只能用于计算加法,且只能从0开始计算,LongAccumulator提供了自定义的函数操作。

LongAccumulator构造方法需要传入一个函数:

public LongAccumulator(LongBinaryOperator accumulatorFunction,long identity) {this.function = accumulatorFunction;base = this.identity = identity;
}

LongBinaryOperator需要传入一个left、一个right,并且有一个返回值:

@FunctionalInterface
public interface LongBinaryOperator {long applyAsLong(long left, long right);
}

LongAccumulator的accumulate方法,用于将传入的参数与当前的参数,使用函数进行计算。

LongAccumulator longAccumulator = new LongAccumulator(new LongBinaryOperator()
{@Overridepublic long applyAsLong(long left, long right){return left + right;}
},0);longAccumulator.accumulate(1);//0 + 1 = 1
longAccumulator.accumulate(3);//1 + 3 = 4System.out.println(longAccumulator.get());

5、synchronized、AtomicLong、LongAdder、LongAccumulator性能对比

通过结果我们可以发现,LongAdder、LongAccumulator在高并发场景下,有着极高的性能,数倍于AtomicLong。

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;public synchronized void clickBySynchronized(){number++;}AtomicLong atomicLong = new AtomicLong(0);public void clickByAtomicLong(){atomicLong.getAndIncrement();}LongAdder longAdder = new LongAdder();public void clickByLongAdder(){longAdder.increment();}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;public static void main(String[] args) throws InterruptedException{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());}
}

----costTime: 4059 毫秒 clickBySynchronized: 50000000
----costTime: 716 毫秒 clickByAtomicLong: 50000000
----costTime: 73 毫秒 clickByLongAdder: 50000000
----costTime: 57 毫秒 clickByLongAccumulator: 50000000

七、LongAdder源码分析

LongAdder为什么在高并发下保持良好性能?LongAdder源码详细分析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/154858.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

功率半导体器件静态参数测试都测哪些内容?

功率半导体器件如今已成为不可或缺的元件&#xff0c;在通信、电力电子等领域得到广泛应用。而对其性能参数的测试也是必不可少的&#xff0c;是对半导体性能、质量的保障。半导体测试参数包含静态测试参数和动态测试参数&#xff0c;本文将介绍半导体分立器件静态测试参数的相…

【网络安全 ---- 靶场搭建】凡诺企业网站管理系统靶场详细搭建过程(asp网站,练习Access数据库的 sql注入)

一&#xff0c;资源下载 百度网盘资源下载链接&#xff1a;百度网盘 请输入提取码百度网盘为您提供文件的网络备份、同步和分享服务。空间大、速度快、安全稳固&#xff0c;支持教育网加速&#xff0c;支持手机端。注册使用百度网盘即可享受免费存储空间https://pan.baidu.com…

Android Studio Flutter真机调试错误

错误&#xff1a;Could not locate aapt. Please ensure you have the Android buildtools installed. No application found for TargetPlatform.android_arm64. Is your project missing an android/app/src/main/AndroidManifest.xml? Consider running "flutter crea…

京东运营数据分析:2023年8月京东饮料行业品牌销售排行榜

鲸参谋监测的京东平台8月份饮料市场销售数据已出炉&#xff01; 8月份&#xff0c;饮料市场整体销售下滑。根据鲸参谋电商数据分析平台的相关数据显示&#xff0c;今年8月&#xff0c;京东平台饮料市场的总销量将近820万&#xff0c;环比下滑约8%&#xff0c;同比下滑约20%&am…

Docker与Serverless计算的集成: Docker容器如何与Serverless计算结合。

文章目录 1. Docker容器的可移植性2. Serverless计算的自动伸缩性3. 使用Serverless与Docker容器a. 自托管Serverless平台b. 使用容器服务 4. 使用案例&#xff1a;图像处理服务5. 结论 &#x1f388;个人主页&#xff1a;程序员 小侯 &#x1f390;CSDN新晋作者 &#x1f389;…

flex布局在多层嵌套时,内层设置了justify-content: space-between;不生效问题

内层的地址和时间这一行&#xff0c;设置了justify-content: space-between;但并不生效&#xff0c;原因是要在上一层.center 设置 flex:1;&#xff08; 重点&#xff09; 经常忘记&#xff0c;特在此记录一下&#xff0c;以下是代码 <view class"index-card" c…

3D 人体研究

SynBody: Synthetic Dataset with Layered Human Models for 3D Human Perception and Modeling SynBody 是一个新的合成数据集&#xff0c;旨在推动 3D 人体研究的多样性和标注质量。它具有以下三个吸引人的特点&#xff1a; 服装参数化人体模型&#xff1a;可以生成各种不同…

数学建模、统计建模、计量建模整体框架的理解以及建模的步骤

数学建模、统计建模、计量建模整体框架的理解以及建模的步骤 引言正文模型的设定模型的估计建模中可能遇到的四种数据类型 模型的检验模型的应用 最后 引言 这篇博客主要写给统计或者数学专业的小白&#xff0c;以供快速上手建模比赛&#xff1b;本人将在这里整合参加建模比赛…

c语言:通讯录管理系统(文件版本)

前言&#xff1a;在大多数高校内&#xff0c;都是通过设计一个通讯录管理系统来作为c语言课程设计&#xff0c;通过一个具体的系统设计将我们学习过的结构体和函数等知识糅合起来&#xff0c;可以很好的锻炼学生的编程思维&#xff0c;本文旨在为通讯录管理系统的设计提供思路和…

SpringBoot配置输出的日志文件

SpringBoot配置输出的日志文件 1、无需导入依赖&#xff0c;因为我们创建springboot时&#xff0c;导入的关于springboot的依赖中已经包含了。 2、我们在项目的resources 资源文件下创建logback.xml文件&#xff0c;文件内容如下 作用&#xff1a; 如果是开发时启动的项目&a…

Python自动化测试框架pytest的详解安装与运行

这篇文章主要为大家介绍了Python自动化测试框架pytest的简介以及安装与运行&#xff0c;有需要的朋友可以借鉴参考下希望能够有所帮助&#xff0c;祝大家多多进步 1. pytest的介绍 pytest是一个非常成熟的全功能的python测试工具&#xff0c;它主要有以下特征&#xff1a; 简…

Java List 中存不同的数据类型

在最近的实践中&#xff0c;有人突然问了一个问题&#xff1a; 在 Java 的 List 中可以存不同的数据类型吗&#xff1f; 这个问题突然给问到了&#xff0c;我们都知道 Java 中的 List 中存的是对象&#xff0c;通常我们定义都会这样的定义&#xff1a; List<String> t…

大端字节序存储 | 小端字节序存储介绍

为什么存储的顺序有些变动呢&#xff1f; 大小端的介绍 我们在创建变量时&#xff0c;操作系统就会给你分配空间&#xff0c;比如你创建了【short/int/double/float】的变量&#xff0c;这些变量的类型都是大于1个字节的&#xff0c;操作系统会根据你这个变量的类型&#xff…

Java基础面试-JDK JRE JVM

详细解释 JDK&#xff08;Java Devalpment Kit&#xff09;java 开发工具 JDK是Java开发工具包&#xff0c;它是Java开发者用于编写、编译、调试和运行Java程序的核心组件。JDK包含了Java编程语言的开发工具和工具集&#xff0c;以及Java标准库和其他一些必要的文件。JDK中的…

SpringBoot 如何使用 Grafana 进行可视化监控

使用Spring Boot Sleuth进行分布式跟踪 在现代分布式应用程序中&#xff0c;跟踪请求和了解应用程序的性能是至关重要的。Spring Boot Sleuth是一个分布式跟踪解决方案&#xff0c;它可以帮助您在分布式系统中跟踪请求并分析性能问题。本文将介绍如何在Spring Boot应用程序中使…

HomeView/主页 的实现

1. 创建数据模型 1.1 创建货币模型 CoinModel.swift import Foundation// GoinGecko API info /*URL:https://api.coingecko.com/api/v3/coins/markets?vs_currencyusd&ordermarket_cap_desc&per_page250&page1&sparklinetrue&price_change_percentage24…

手动抄表和自动抄表优缺点对比

随着科技的发展&#xff0c;自动抄表技术已经越来越成熟&#xff0c;被广泛应用于各个领域。然而&#xff0c;手动抄表在一些特定场景下仍然具有一定的优势。本文将从手动抄表和自动抄表的优缺点入手&#xff0c;对比分析它们的应用场景和使用价值。 1.成本低&#xff1a;手动抄…

Android Studio: unrecognized Attribute name MODULE

错误完整代码&#xff1a; &#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd; (1.8.0_291) &#xfffd;г&#xfffd;&#xfffd;&#xfffd;&#xfffd;쳣&#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xff…

web安全漏洞

1.什么是Web漏洞   WEB漏洞通常是指网站程序上的漏洞&#xff0c;可能是由于代码编写者在编写代码时考虑不周全等原因而造成的漏洞。如果网站存在WEB漏洞并被黑客攻击者利用&#xff0c;攻击者可以轻易控制整个网站&#xff0c;并可进一步提前获取网站服务器权限&#xff0c;…

Linux Centos7 下使用yum安装的nginx平滑升级

1. 查看当前nginx版本 1nginx -v2. 查看centos版本 1cat /etc/redhat-release3. 创建一个新的文件nginx.repo&#xff0c;其中第三行的7是因为我的centos版本是7点多的&#xff0c;你看自己是多少就改多少 1vim /etc/yum.repos.d/nginx.repo23[nginx]4namenginx repo 5baseu…