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.mzph.cn/news/101159.shtml

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

相关文章

ubuntu疑难杂症

1.ubuntu 使用apt 安装软件时提示出现不能获得lock $ sudo rm /var/lib/dpkg/lock$ sudo dpkg --configure -a$ sudo rm /var/lib/apt/lists/locksudo rm /var/lib/dpkg/lock sudo rm /var/lib/dpkg/lock-frontend sudo rm /var/cache/apt/archives/lock

手机待办事项app哪个好?

手机是日常很多人随身携带的设备&#xff0c;手机除了拥有通讯功能外&#xff0c;还能帮助大家高效管理日常工作&#xff0c;借助手机上的待办事项提醒APP可以快速地帮助大家规划日常事务&#xff0c;提高工作的效率。 过去&#xff0c;我也曾经在寻找一款能够将工作任务清晰罗…

区块链技术的飞跃: 2023年的数字革命

随着时代的推进和技术的不断创新&#xff0c;2023年成为区块链技术飞跃发展的一年。区块链&#xff0c;一个曾经只是数字货币领域的技术&#xff0c;现在已经逐渐渗透到各个行业&#xff0c;成为推动数字经济发展的重要力量。在这个数字革命的时代&#xff0c;我们探讨区块链技…

水库大坝除险加固安全监测系统解决方案

一、系统背景 为贯彻落实《办公厅关于切实加强水库除险加固和运行管护工作的通知》&#xff08;〔2021〕8号&#xff09;要求&#xff0c;完成“十四五”小型病险水库除险加固、雨水情测报和大坝安全监测设施建设任务&#xff0c;规范项目管理&#xff0c;消除安全隐患&#xf…

Hadoop设置hdfs全局指令

在终端进入用户个人环境变量配置文件 vim ~/.bashrc 然后添加如下内容 export PATH$PATH:/usr/local/hadoop/bin 添加到你的hadoop下载目录的bin目录为止就可以了 重新激活一下配置文件 source ~/.bashrc hdfs有专属于自己的文件存储目录,加上特殊的指令就可以箱终端一…

Python“梦寻”淘宝天猫店铺所有数据接口,淘宝店铺所有商品数据API接口,淘宝API接口申请指南(含代码示例)

获取淘宝店铺所有商品数据的接口可以通过淘宝开放平台获取。 具体操作步骤如下&#xff1a; 在淘宝开放平台注册成为开发者&#xff0c;并创建一个应用&#xff0c;获取到所需的 App Key 和 App Secret 等信息。使用获取到的 App Key 和 App Secret&#xff0c;进行签名和认证…

@MultipartConfig注解

前言&#xff1a; 在学习Javaweb的Servlet文件上传和下载的过程中&#xff0c;我们会遇到一个特殊的注解---MultipartConfig。 MultipartConfig的适用情况&#xff1a; 1.文件上传: 当您的应用程序需要接收用户上传的文件时&#xff0c;可以在相应的 Servlet 上使用 Multipart…

C++ - 智能指针 - auto_ptr - unique_ptr - std::shared_ptr - weak_ptr

前言 C当中的内存管理机制需要我们自己来进行控制&#xff0c;比如 在堆上 new 了一块空间&#xff0c;那么当这块空间不需要再使用的时候。我们需要手动 delete 掉这块空间&#xff0c;我们不可能每一次都会记得&#xff0c;而且在很大的项目程序当中&#xff0c;造成内存泄漏…

milvus测试

milvus测试 目标 其实&#xff0c;我应该弄明白他的输入输出分别是什么&#xff1f; 输入是图片&#xff0c;图片经过ml模型进行特征提取&#xff0c;再在milvus中进行存储或者检索 部署 ✘ delldell-Precision-3630-Tower  /nvme/baum/git-project/milvus   master …

SpringBoot 如何使用 Ehcache 作为缓存

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

选择适合变更管理的产品开发工具的要点和建议

什么是变更管理&#xff1f; 变更管理是指导组织改进的学科。由于可观察到的行为变化&#xff0c;它会导致永久性变化。它确保您的组织以彻底、有序和可持续的方式学习和改进。成功的改进项目需要个人和团队保持一致&#xff0c;他们有共同的愿景&#xff0c;他们知道如何定义…

MidJourney | AI绘画也有艺术

免费绘画&#xff0c;体验更多AI可关&注公&众&号&#xff1a;AI研究工厂

大数据学习(2)Hadoop-分布式资源计算hive(1)

&&大数据学习&& &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 承认自己的无知&#xff0c;乃是开启智慧的大门 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4dd;支持一下博>主哦&#x…

如何将gif变成视频?3个转换方法

如何将gif变成视频&#xff1f;没错&#xff0c;GIF是一种动态图片格式&#xff0c;与视频在本质上有所区别。在一些自媒体平台上&#xff0c;我们无法直接分享GIF格式的图片&#xff0c;但可以将其转换为视频格式后再进行分享。因此&#xff0c;当我们想要分享我们喜欢的GIF图…

香港硬防服务器的防御有什么优缺点?

​  在选择服务器时&#xff0c;安全性是一个重要的考虑因素。而对于那些需要高级防御功能的用户来说&#xff0c;香港硬防服务器可能是一个不错的选择。它也有一些优缺点需要考虑。 香港硬防服务器优点&#xff1a; 强大的硬件资源&#xff1a;香港硬防服务器拥有足够的硬件…

nginx如何安装 以及nginx的配置文件

Nginx 网站服务 是一个高性能 轻量级web服务软件&#xff0c; 高新能&#xff1a;对http并发连接的处理能很高&#xff0c;单台处理器可支持30000-50000个并发请求&#xff0c;&#xff08;一般设置在20000个左右&#xff09; 轻量级&#xff1a;nginx软件很小&#xff0c;安装…

ChromeDriver驱动最新版下载

下载地址ChromeDriver - WebDriver for Chrome - Downloads selenium.common.exceptions.SessionNotCreatedException: Message: session not created: This version of ChromeDriver only supports Chrome version 113 Current browser version is 117.0.5938.150 with binar…

vim基础指令(自用)

这个是自己随便写的&#xff0c;类似于笔记 vim 多模式编辑器 查看指令&#xff1a; gg&#xff1a; 定位光标到最开始行 shift(按)g 定位到最结尾行 nshift(按)g 定位到任意行 shift&#xff04; 定位到本行结尾 0 定位到本行开头 w&#xff1a;跨单词移动 h.j.k,l: 左下上右 …

各种业务场景调用API代理的API接口教程

API代理的API接口在各种业务场景中具有广泛的应用&#xff0c;本文将介绍哪些业务场景可以使用API代理的API接口&#xff0c;并提供详细的调用教程和代码演示&#xff0c;同时&#xff0c;我们还将讨论在不同场景下使用API代理的API接口所带来的好处。 哪些业务场景可以使用API…

42. QT中开发Android配置QFtp功能时遇到的编译问题

1. 说明 此问题仅适用在QT中开发Android程序时&#xff0c;需要适用QFtp功能的情况。一般情况下&#xff0c;如果开发的是Windows或者Linux系统下的程序&#xff0c;可能不会出现该问题。 2. 问题 【Android】在将QFtp的相关代码文件加入到项目中后&#xff0c;编译项目时会…