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

 更新时间:2021年05月26日 16:07:04   作者:it00zyq  
今天给大家总结了一下Java常用的原子类,文中有非常详细的介绍及代码示例,对正在学习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常用原子类内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • springboot中使用redis并且执行调试lua脚本

    springboot中使用redis并且执行调试lua脚本

    今天有个项目需要使用redis,并且有使用脚本的需求,本文主要介绍了springboot中使用redis并且执行调试lua脚本,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-04-04
  • elasticsearch+logstash并使用java代码实现日志检索

    elasticsearch+logstash并使用java代码实现日志检索

    这篇文章主要介绍了elasticsearch+logstash并使用java代码实现日志检索,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-02-02
  • Java检查日期字符串是否合法的方法总结

    Java检查日期字符串是否合法的方法总结

    后端接口在接收数据的时候,都需要进行检查。检查全部通过后,才能够执行业务逻辑。本文总结了四个Java检查日期字符串是否合法的方法,感兴趣的可以了解一下
    2022-10-10
  • Spring框架JdbcTemplate数据库事务管理完全注解方式

    Spring框架JdbcTemplate数据库事务管理完全注解方式

    这篇文章主要介绍了Spring框架JdbcTemplate数据库事务管理及完全注解方式,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • Java中的WeakHashMap简析

    Java中的WeakHashMap简析

    这篇文章主要介绍了Java中的WeakHashMap简析,Map 的子类常见的有 HashMap、Hashtable、ConcurrentHashMap、LinkedHashMap 等,WeakHashMap,直译就是,虚弱的 HashMap,从名字可得知其和 HashMap 有关,需要的朋友可以参考下
    2023-09-09
  • Springcloud hystrix服务熔断和dashboard如何实现

    Springcloud hystrix服务熔断和dashboard如何实现

    这篇文章主要介绍了Springcloud hystrix服务熔断和dashboard如何实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-12-12
  • SpringCloud OpenFeign 自定义响应解码器的问题记录

    SpringCloud OpenFeign 自定义响应解码器的问题记录

    我们在使用 Spring Cloud 微服务的时候,通常将返回结果使用一个JsonResult 类进行封装,本文重点介绍SpringCloud OpenFeign 自定义响应解码器的问题记录,感兴趣的朋友跟随小编一起看看吧
    2024-06-06
  • java中复杂查询sql语句该怎么写

    java中复杂查询sql语句该怎么写

    我们知道在java连接数据库之后,需要数据库的sql语句,下面这篇文章主要给大家介绍了关于java中复杂查询sql语句该怎么写的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-11-11
  • SpringMVC中处理Ajax请求的示例

    SpringMVC中处理Ajax请求的示例

    本篇文章给大家介绍SpringMVC中处理Ajax请求的示例,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2023-11-11
  • spring @Transactional 无效的解决方案

    spring @Transactional 无效的解决方案

    这篇文章主要介绍了spring @Transactional 无效的解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03

最新评论