详细总结Java中常用的原子类

网友投稿 265 2023-01-12

详细总结Java中常用的原子类

一、什么是原子类

java中提供了一些原子类,原子类包装了一个变量,并且提供了一系列对变量进行原子性操作的方法。我们在多线程环境下对这些原子类进行操作时,不需要加锁,大大简化了并发编程的开发。

二、原子类的底层实现

目前Java中提供的原子类大部分底层使用了CAS锁(CompareAndSet自旋锁),如AtomicInteger、AtomicLong等;也有使用了分段锁+CAS锁的原子类,如LongAdder等。

三、常用的原子类

3.1 AtomicInteger与AtomicLong

AtomicInteger与AtomicLong的底层实现与用法基本相同,不同点在于AtomicInteger包装了一个Integer型变量,而AtomicLong包装了一个Long型变量。

AtomicInteger与AtomicLong的底层实现都使用了CAS锁。

import java.util.concurrent.atomic.AtomicInteger;

import java.util.concurrent.atomic.AtomicLong;

/**

* @author IT00ZYQ

* @date 2021/5/24 15:33

**/

public class T13_AtomicInteger {

private static AtomicInteger atomicInteger = new AtomicInteger();

private static AtomicLong atomicLong = new AtomicLong();

private static Integer integer = 0;

private static Long lon = 0L;

http://public static void main(String[] args) {

// 创建10个线程,分别对atomicInteger、atomicLong、integer、lon进行1000次增加1的操作

// 如果操作是原子性的,那么正确结果 = 10 * 1000 = 10000

Thread[] threads = new Thread[10];

for (int i = 0; i < 10; i++) {

threads[i] = new Thread(() -> {

for (int j = 1; j <= 1000; j++) {

atomicInteger.incrementAndGet();

atomicLong.incrementAndGet();

integer ++;

lon ++;

}

});

}

// 启动线程

for (Thread thread : threads) {

thread.start();

}

// 保证10个线程运行完成

try {

for (Thread thread : threads) {

thread.join();

}

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("AtomicInteger的结果:" + atomicInteger);

System.out.println("AtomicLong的结果:" + atomicLong);

System.out.println("Integer的结果:" + integer);

System.out.println("Long的结果:" + lon);

}

}

运行结果:

AtomicInteger的结果:10000

AtomicLong的结果:10000

Integer的结果:4880

Long的结果:4350

Process finished with exit code 0

多次运行发现原子类AtomicInteger与AtomicLong每次都能得到正确的结果10000,但是非原子类Integer与Long一般情况下都达不到10000,每次的结果也可能不一样。

3.2 LongAdder

LongAdder的底层实现使用了分段锁,每个段使用的锁是CAS锁,所以LongAdder的底层实现是分段锁+CAS锁。

在上面的程序添加了一个LongAdder变量进行测试

import java.util.concurrent.atomic.AtomicInteger;

import java.util.concurrent.atomic.AtomicLong;

import java.util.concurrent.atomic.LongAdder;

/**

* @author IT00ZYQ

* @date 2021/5/24 15:33

**/

public class T13_AtomicInteger {

private static AtomicInteger atomicInteger = new AtomicInteger();

private static AtomicLong atomicLong = new AtomicLong();

private static LongAdder longAdder = new LongAdder();

private static Integer integer = 0;

private static Long lon = 0L;

public static void main(String[] args) {

// 创建10个线程,分别对atomicInteger、atomicLong、integer、lon进行1000次增加1的操作

// 如果操作是原子性的,那么正确结果 = 10 * 1000 = 10000

Thread[] threads = new Thread[10];

for (int i = 0; i < 10; i++) {

threads[i] = new Thread(() -> {

for (int j = 1; j <= 1000; j++) {

atomicInteger.incrementAndGet();

atomicLong.incrementAndGet();

integer ++;

lon ++;

longAdder.increment();

}

});

}

// 启动线程

for (Thread thread : threads) {

thread.start();

}

// 保证10个线程运行完成

try {

for (Thread thread : threads) {

thread.join();

}

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("AtomicInteger的结果:" + atomicInteger);

System.out.println("AtomicLong的结果:" + atomicLong);

System.out.println("Integer的结果:" + integer);

System.out.println("Long的结果:" + lon);

System.out.println("LongAdder的结果:" + longAdder);

}

}

运行结果:

AtomicInteger的结果:10000

AtomicLong的结果:10000

Integer的结果:6871

Long的结果:6518

LongAdder的结果:10000

Process finished with exit code 0

LongAdder类也是能够正确输出结果的。

四、原子类的性能测试

4.1 测试程序

package juc;

import java.util.concurrent.atomic.AtomicLong;

import java.util.concurrent.atomic.LongAdder;

/**

* @author IT00ZYQ

* @date 2021/5/24 15:51

**/

public class T14_AtomicClassPerformance {

private static AtomicLong atomicLong = new AtomicLong();

private static LongAdder longAdder = new LongAdder();

/**

* 线程数

*/

private static final int THREAD_COUNT = 100;

/**

* 每次线程循环操作次数

*/

private static final int OPERATION_COUNT = 10000;

public static void main(String[] args) {

Thread[] threads = new Thread[THREAD_COUNT];

// 创建对AtomicLong进行操作的线程

for (int i = 0; i < THREAD_COUNT; i++) {

threads[i] = new Thread(() -> {

for (int j = 0; j < OPERATION_COUNT; j++) {

atomicLong.incrementAndGet();

}

});

}

long start1 = System.currentTimeMillis();

// 启动线程

for (Thread thread : threads) {

thread.start();

}

// 保证线程运行完成

try {

for (Thread thread : threads) {

thread.join();

}

} catch (InterruptedException e) {

e.printStackTrace();

}

long end1 = System.currentTimeMillis();

// 创建对LongAdder进行操作的线程

for (int i = 0; i < THREAD_COUNT; i++) {

threads[i] = new Thread(() -> {

for (int j = 0; j < OPERATION_COUNT; j++) {

longAdder.increment();

}

});

}

long start2 = System.currentTimeMillis();

// 启动线程

for (Thread thread : threads) {

thread.start();

}

// 保证线程运行完成

try {

for (Thread thread : threads) {

thread.join();

}

} catch (InterruptedException e) {

e.printStackTrace();

}

long end2 = System.currentTimeMillis();

System.out.println("AtomicLong运行时间: " + (end1 - start1) + "ms, 运行结果:" + atomicLong);

System.out.println("LongAdder运行时间: " + (end2 - start2) + "ms, 运行结果:" + longAdder);

}

}

4.2 测试结果

THREAD_COUNT = 100, OPERATION_COUNT = 1000时的运行结果

AtomicLong运行时间: 40ms, 运行结果:100000

LongAdder运行时间: 57ms, 运行结果:100000

Process finished with exit code 0

THREAD_COUNT = 100, OPERATION_COUNT = 10000时的运行结果

AtomicLong运行时间: 108ms, 运行结果:1000000

LongAdder运行时间: 85ms, 运行结果:1000000

Process finished withhttp:// exit code 0

THREAD_COUNT = 100, OPERATION_COUNT = 1000000时的运行结果

AtomicLong运行时间: 6909ms, 运行结果:100000000

LongAdder运行时间: 468ms, 运行结果:100000000

Process finished with exit code 0

THREAD_COUNT = 10, OPERATION_COUNT = 1000000时的运行结果

AtomicLong运行时间: 788ms, 运行结果:10000000

LongAdder运行时间: 162ms, 运行结果10000000

Process finished with exit code 0

4.3 结果分析

当THREAD_COUNT * OPERATION_COUN足够小时,AtomicInteger的性能会略高于LongAdder,而随着THREAD_COUNT * OPERATION_COUN的增加,LongAdder的性能更高,THREAD_COUNT * OPERATION_COUN足够大时,LongAdder的性能远高于AtomicInteger。

4.4 底层实现分析

AtomicLong的原子性自增操作,是通过CAS实现的。在竞争线程数较少且每个线程的运行所需时间较短的情况下,这样做是合适的。但是如果线程竞争激烈,会造成大量线程在原地打转、不停尝试去修改值,但是老是发现值被修改了,于是继续自旋。 这样浪费了大量的CPU资源。

LongAdder在竞争激烈时,多个线程并不会一直自旋来修改值,而是采用了分段的思想,各个线程会分散累加到自己所对应的Cell[]数组的某一个数组对象元素中,而不会大家共用一个,把不同线程对应到不同的Cell中进行修改,降低了对临界资源的竞争。本质上,是用空间换时间。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:星外免费api接口(外设api)
下一篇:众源物流查询(物流公共信息平台)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~