详细总结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; 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 with 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中进行修改,降低了对临界资源的竞争。本质上,是用空间换时间。
到此这篇关于详细总结Java中常用的原子类的文章就介绍到这了,更多相关Java常用原子类内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!
相关文章
- 这篇文章主要介绍了如何利用java语言实现经典《复杂迷宫》游戏,文中采用了swing技术进行了界面化处理,感兴趣的小伙伴可以动手试一试...2022-02-01
java 运行报错has been compiled by a more recent version of the Java Runtime
java 运行报错has been compiled by a more recent version of the Java Runtime (class file version 54.0)...2021-04-01- 这篇文章主要介绍了在java中获取List集合中最大的日期时间操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-15
- 这篇文章主要介绍了教你怎么用Java获取国家法定节假日,文中有非常详细的代码示例,对正在学习java的小伙伴们有非常好的帮助,需要的朋友可以参考下...2021-04-23
- 这篇文章主要介绍了Java如何发起http请求的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-31
- 说起C#和Java这两门语言(语法,数据类型 等),个人以为,大概有90%以上的相似,甚至可以认为几乎一样。但是在工作中,我也发现了一些细微的差别...2020-06-25
- 这篇文章主要介绍了解决Java处理HTTP请求超时的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-03-29
- 这篇文章主要介绍了java 判断两个时间段是否重叠的案例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-15
- 这篇文章主要介绍了超简洁java实现双色球若干注随机号码生成(实例代码),本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-04-02
java 画pdf用itext调整表格宽度、自定义各个列宽的方法
这篇文章主要介绍了java 画pdf用itext调整表格宽度、自定义各个列宽的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-01-31- 这篇文章主要介绍了Java生成随机姓名、性别和年龄的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-10-01
- 这篇文章主要介绍了java正则表达式判断前端参数修改表中另一个字段的值,需要的朋友可以参考下...2021-05-07
Java使用ScriptEngine动态执行代码(附Java几种动态执行代码比较)
这篇文章主要介绍了Java使用ScriptEngine动态执行代码,并且分享Java几种动态执行代码比较,需要的朋友可以参考下...2021-04-15- 这篇文章主要介绍了Java开发实现人机猜拳游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-08-03
- 这篇文章主要介绍了Java List集合返回值去掉中括号('[ ]')的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-29
Java中lombok的@Builder注解的解析与简单使用详解
这篇文章主要介绍了Java中lombok的@Builder注解的解析与简单使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-01-06- 下面小编就为大家带来一篇java中String类型变量的赋值问题介绍。小编觉得挺不错的。现在分享给大家,给大家一个参考。...2016-03-28
Java 8 Stream 的终极技巧——Collectors 功能与操作方法详解
这篇文章主要介绍了Java 8 Stream Collectors 功能与操作方法,结合实例形式详细分析了Java 8 Stream Collectors 功能、操作方法及相关注意事项,需要的朋友可以参考下...2020-05-20- 这篇文章主要介绍了Java线程池中的各个参数如何合理设置操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-06-19
- 在Java中,我们可以利用多线程来最大化地压榨CPU多核计算的能力,下面这篇文章主要给大家介绍了关于java中多线程与线程池基本使用的相关资料,需要的朋友可以参考下...2021-09-13