AtomicInteger 核心源码解析(下)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: AtomicInteger 核心源码解析(下)

getAndSet - 设新值,返旧值

因为 value 是 volatile 变量,所以对 value 的 read/write 具备 happens-before 关系,所以一个很容易想到的实现为:

AtomicInteger counter = new AtomicInteger();
int oldV = counter.get();
counter.set(10);

但该实现是错的,因为 counter.get() 与 counter.set(10) 之间可能插入其他线程的 set,所以 oldV 不能保证是 set(10) 执行时的 value 值,当然通过锁将 get 与 set(10) 变成原子操作可以满足需求,但我们使用 AtomicInteger 就是为了避免使用锁,所以也不能这么做.

于是有了getAndSet:

image.png

将 set 和 get 合并成一个原子操作,同时避免使用锁,依旧借助 unsafe 实现。

基本的运算操作

自增

  • 以原子方式将当前值增加一(i++)
  • image.png
  • 以原子方式将当前值增加一(++i)
  • image.png

自减

  • 以原子方式将当前值减一(i–)
  • image.png
  • 以原子方式将当前值减一(–i)
  • image.png
  • 都是基于 Unsafe.getAndAddInt 实现的,该方法实现 value 加操作,且返回旧值。

任意值的加减

image.png

CAS 操作

compareAndSet

image.png

getAndSet 无脑更新 value ,并发场景下不会一直如此简单,有时要求 value 满足特定条件时才设置,这是非常典型的原子复合操作


检查某条件是否成立

根据条件成功、失败执行不同操作

在业务代码中,这种操作一般用锁实现,但 AtomicInteger 原生提供的 compareAndSet 无锁完美解决.

只有 value 的当前值等于 expect 时,才把 value 设置为 update,同时如果设置成功则返回 true,否则返回 false。

借助返回值可以检测方法的执行结果,因此可以在循环操作中不断执行 compareAndSet,直到成功,在线程池的源码中,很多方法都是这种套路。

weakCompareAndSet

  • 弱化版compareAndSet,可能会虚假地失败,并且不提供排序保证,因此,很少是compareAndSet的适当替代方法,JDK8源码中未曾使用过它,因为二者在 Java 源码层次是一模一样的.
  • image.png

总结

AtomicIntger 的关键是 compareAndSet 方法,基于它可实现乐观的无锁算法.其妙用在 线程池中有着淋漓尽致地体现

目录
相关文章
|
10天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
36 2
|
10天前
|
存储 安全 Linux
Golang的GMP调度模型与源码解析
【11月更文挑战第11天】GMP 调度模型是 Go 语言运行时系统的核心部分,用于高效管理和调度大量协程(goroutine)。它通过少量的操作系统线程(M)和逻辑处理器(P)来调度大量的轻量级协程(G),从而实现高性能的并发处理。GMP 模型通过本地队列和全局队列来减少锁竞争,提高调度效率。在 Go 源码中,`runtime.h` 文件定义了关键数据结构,`schedule()` 和 `findrunnable()` 函数实现了核心调度逻辑。通过深入研究 GMP 模型,可以更好地理解 Go 语言的并发机制。
|
23天前
|
消息中间件 缓存 安全
Future与FutureTask源码解析,接口阻塞问题及解决方案
【11月更文挑战第5天】在Java开发中,多线程编程是提高系统并发性能和资源利用率的重要手段。然而,多线程编程也带来了诸如线程安全、死锁、接口阻塞等一系列复杂问题。本文将深度剖析多线程优化技巧、Future与FutureTask的源码、接口阻塞问题及解决方案,并通过具体业务场景和Java代码示例进行实战演示。
40 3
|
1月前
|
存储
让星星⭐月亮告诉你,HashMap的put方法源码解析及其中两种会触发扩容的场景(足够详尽,有问题欢迎指正~)
`HashMap`的`put`方法通过调用`putVal`实现,主要涉及两个场景下的扩容操作:1. 初始化时,链表数组的初始容量设为16,阈值设为12;2. 当存储的元素个数超过阈值时,链表数组的容量和阈值均翻倍。`putVal`方法处理键值对的插入,包括链表和红黑树的转换,确保高效的数据存取。
56 5
|
1月前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
113 5
|
1月前
|
XML Java 数据格式
Spring底层架构源码解析(二)
Spring底层架构源码解析(二)
|
1月前
|
算法 Java 程序员
Map - TreeSet & TreeMap 源码解析
Map - TreeSet & TreeMap 源码解析
34 0
|
1月前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
70 0
|
1月前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
57 0
|
1月前
|
存储 Java C++
Collection-PriorityQueue源码解析
Collection-PriorityQueue源码解析
62 0
下一篇
无影云桌面