Java多线程场景解析volatile和AtomicLong区别原理

 更新时间:2023年09月22日 11:38:46   作者:赵KK日常技术记录  
这篇文章主要为大家介绍了Java中volatile和AtomicLong的区别原理示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

【摘要】

随着多线程编程的普及,volatile和AtomicLong这两个关键字被越来越多的使用。但是很多人并不理解它们的原理以及区别。今天我就来通过一个典型的场景,讲解 volatile 和 AtomicLong 的用法、原理以及区别,希望可以帮助大家更好地使用这两者。

我们假设有这样一个多线程场景:存在一个长整型变量,多个线程同时进行读写操作。为了保证线程安全,我们通常会进行同步,但是同步操作又会影响效率。这个时候我们就可以考虑用 volatile 或者 AtomicLong 来优化。

volatile

先说说 volatile,它可以保证变量的可见性,具体来说就是当一个线程修改了 volatile 变量,新值对其他线程立即可见。这是因为volatile变量直接操作主内存,而不是线程的工作内存。

java
public class VolatileExample {
  volatile long count = 0;
  public void increment() {
    count++; 
  }
  public long getCount() {
    return count;
  }
}

这里通过 volatile 关键字,可以保证多个线程都能读取到 count 的最新的值。但是,volatile 并不能保证原子性。什么意思呢?我们来看看下面的代码:

java
public void increment() {
  count++;
}
// 这里的递增操作并不是原子的,实际会分为三步
// 1. 读取count值 
// 2. 计算值+1
// 3. 写入新的值

也就是说某一个时刻,可能有两个线程同时读取了 count 值,然后分别增加,这就破坏了原子性。要解决这个问题,我们就需要 AtomicLong 了。

AtomicLong

AtomicLong 通过 CAS (Compare And Swap) 算法实现了对长整型的原子操作。看一个例子:

java
AtomicLong atomicCount = new AtomicLong();
public void increment() {
  atomicCount.getAndIncrement(); 
}

getAndIncrement()会先获取当前的值,然后原子地增加,最后返回旧值。这样多个线程调用的时候可以保证原子性。AtomicLong 的 CAS 原理是通过循环调用 CAS 指令,直到更新成功为止:

java
do {
  // 获取旧的预期值 
  oldValue = atomicCount.get();
  // 用CAS指令更新值,如果预期值一样才更新    
  success = compareAndSet(oldValue, oldValue + 1); 
// 如果失败,继续循环执行CAS  
} while(!success);

通过 CAS 不断尝试更新,直到成功为止。

volatile 和 AtomicLong 的区别

总结一下 volatile 和 AtomicLong 的区别

1. volatile 保证可见性,AtomicLong 保证原子性

2. volatile 不会对代码产生线程锁,AtomicLong 会通过 CAS 产生线程开销

3. volatile 是通过直接操作主内存实现,AtomicLong 是通过 CAS 实现那么什么时候该用 volatile,什么时候该用 AtomicLong 呢?

  • 如果只要保证可见性,那用 volatile 即可。
  • 如果需要原子操作,如对数字进行递增操作,这时就需要用 AtomicLong。

当然,synchronized和Lock也能保证原子性,但是通过 CAS 实现的 AtomicLong 性能会更好一些。

以上就是本文的全部内容了,通过一个典型多线程场景讲解了 volatile 和 AtomicLong 的用法、原理以及区别。这两个关键字非常有用,正确理解后可以充分利用它们带来的优势,更多关于 Java volatile AtomicLong的资料请关注脚本之家其它相关文章!

相关文章

  • Springboot引入拦截器并放行swagger代码实例

    Springboot引入拦截器并放行swagger代码实例

    这篇文章主要介绍了Springboot引入拦截器并放行swagger代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • Java上传文件图片到服务器的方法

    Java上传文件图片到服务器的方法

    这篇文章主要为大家详细介绍了Java上传文件图片到服务器的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01
  • Java中Math.round()的用法及说明

    Java中Math.round()的用法及说明

    这篇文章主要介绍了Java中Math.round()的用法及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-02-02
  • SpringBoot整合jasypt加密配置文件敏感信息

    SpringBoot整合jasypt加密配置文件敏感信息

    在项目中我们需要对配置文件的一些敏感信息进行加密处理,比如数据库账户密码,避免直接暴露出来,这种场景常常用于生产环境,我们不想让开发人员知道生产库的密码,有运维人员统一管理,所以本文给大家介绍了SpringBoot整合jasypt加密配置文件敏感信息
    2024-06-06
  • java类中serialVersionUID的作用及其使用

    java类中serialVersionUID的作用及其使用

    这篇文章主要介绍了java类中serialVersionUID的作用及其使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • java中String字符串删除空格的七种方式

    java中String字符串删除空格的七种方式

    在Java中从字符串中删除空格有很多不同的方法,本文主要介绍了java中String字符串删除空格的七种方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08
  • mybatis解析xml配置中${xxx}占位符的代码逻辑

    mybatis解析xml配置中${xxx}占位符的代码逻辑

    本文主要介绍了mybatis解析xml配置中${xxx}占位符的代码逻辑,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧<BR>
    2023-05-05
  • springboot跨域访问cros与@CrossOrigin注解详析

    springboot跨域访问cros与@CrossOrigin注解详析

    这篇文章主要给大家介绍了关于springboot跨域访问cros与@CrossOrigin注解的相关资料,跨域是指不同域名之间相互访问,它是浏览器的同源策略造成的,是浏览器对JavaScript施加的安全限制,需要的朋友可以参考下
    2023-12-12
  • 23种设计模式(6)java装饰者模式

    23种设计模式(6)java装饰者模式

    这篇文章主要为大家详细介绍了23种设计模式之java装饰者模式,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-11-11
  • 10个SpringBoot参数验证你需要知道的技巧分享

    10个SpringBoot参数验证你需要知道的技巧分享

    参数验证很重要,是平时开发环节中不可少的一部分,那么在Spring Boot应用中如何做好参数校验工作呢,本文提供了10个小技巧,你知道几个呢
    2023-03-03

最新评论