并发读源码——AtomicInteger/AtomicLong/AtomicStampedReference

本文主要是介绍并发读源码——AtomicInteger/AtomicLong/AtomicStampedReference,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 1. AtomicInteger描述
  • 2. 源码解析
  • 3. AtomicInteger演示示例
  • 4. 多线程用法

1. AtomicInteger描述

AtomicInteger从名字上看是操作Integer整数的,但Integer是线程不安全的,AtomicInteger是线程安全的。AtomicInteger的作用可以把两个Integer对象的加减乘除等操作变成一个原子操作,如果对Integer的操作不用AtomicInteger,也可以选择用synchronized锁住两个Integer对象的操作,synchronized是重量级锁,悲观锁,资源开销大,而AtomicInteger中的操作方法采用的轻量级锁,乐观锁,下面会进行分析。

2. 源码解析

AtomicInteger的原理就是运用了乐观锁
悲观锁与乐观锁区别:

  • 悲观锁:在并发操作之前认为冲突概率比较大,读操作之前就上锁。比如synchronized关键字
  • 乐观锁:在并发操作之前认为冲突的概率比较小,读操作不上锁,等到写操作时,再判断数据在此期间是否被修改了,如果数据被修改了,则就把数据重新读出来,重新执行该过程;如果原始数据没被修改,就直接把新值写回去。

AtomicInteger的乐观锁是通过Unsafe下的compareAndSwapInt方法执行的,AtomicInteger是通过乐观锁中的自旋锁方式获取锁的(自旋锁:不放弃CPU,空转,不停的重试,典型的就是通过for进行循环)。

package java.util.concurrent.atomic;
import java.util.function.IntUnaryOperator;
import java.util.function.IntBinaryOperator;
import sun.misc.Unsafe;/*** An {@code int} value that may be updated atomically.  See the* {@link java.util.concurrent.atomic} package specification for* description of the properties of atomic variables. An* {@code AtomicInteger} is used in applications such as atomically* incremented counters, and cannot be used as a replacement for an* {@link java.lang.Integer}. However, this class does extend* {@code Number} to allow uniform access by tools and utilities that* deal with numerically-based classes.** @since 1.5* @author Doug Lea
*/
public class AtomicInteger extends Number implements java.io.Serializable {private static final long serialVersionUID = 6214790243416807050L;// setup to use Unsafe.compareAndSwapInt for updatesprivate static final Unsafe unsafe = Unsafe.getUnsafe();/*valueOffset是下面定义的value在AtomicInteger类内存的偏移量,通过这个偏移量可以定位value的值,下面读写AtomicInteger 对象中的值都是valueOffset进行操作的*/private static final long valueOffset;/*因为value是private类型的,是不能直接获取的,因此通过反射形式获取value变量,然后通过java的native方法unsafe.objectFieldOffset获取value相对AtomicInteger类的内存偏移量*/static {try {valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));} catch (Exception ex) { throw new Error(ex); }}private volatile int value;/*初始化时指定value的值*/public AtomicInteger(int initialValue) {value = initialValue;}/*默认初始化方式,value值默认为0,因为value是int类型的*/public AtomicInteger() {}/*获取value值*/public final int get() {return value;}/*修改value值,因为value是volatile类型的,会立即把value中的值刷入主内存,其它线程可以看到修改*/public final void set(int newValue) {value = newValue;}/*lazySet方法现实中很少用,不会立即把value刷入主内存,但最终会刷值,保证最终一致性*/public final void lazySet(int newValue) {unsafe.putOrderedInt(this, valueOffset, newValue);}/*先获取value的值,然后修改value值为新值newValue,整个动作是原子性的,通过unsafe.getAndSetInt方式实现*/public final int getAndSet(int newValue) {return unsafe.getAndSetInt(this, valueOffset, newValue);}/*封装了Unsafe中compareAndSwapInt的native函数,compareAndSet有两个参数,expect指变量的旧值,是读出来的值,写回去的时候希望没有没其他线程修改,所以是expect;第二个参数是update,值变量的新值,修改过的,希望写入的value值。当expect等于变量当前值时,说明在修改期间没有其他线程对此变量进行修改,所以可以成功写入,变量更新为update,返回true,否则返回false*//*compareAndSwapInt有4个参数,第一个参数是AtomictInteger对象,第二个是成员变量value相对AtomictInteger类的内存偏移量,expect和update保持不变*//*** 如果内存中的value成员变量值等于expect的值,就把update值更新到value中,compareAndSwapInt方法是原子的,先比较,如果相等就直接更新(根据value在AtomicInteger中内存偏移量valueOffset来更新)*/public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}/*** weakCompareAndSet方法与compareAndSet类似,但weakCompareAndSet不会插入内存屏障,不能保障volatile的原子性*/public final boolean weakCompareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}/*** 先获取value成员变量的值,然后对value增加1*/public final int getAndIncrement() {return unsafe.getAndAddInt(this, valueOffset, 1);}/*** 先获取value成员变量的值,然后对value减1public final int getAndDecrement() {return unsafe.getAndAddInt(this, valueOffset, -1);}/*** 先获取value成员变量的值,然后把delta加到value上*/public final int getAndAdd(int delta) {return unsafe.getAndAddInt(this, valueOffset, delta);}/*** 先对成员变量value增加1,然后把修改后的值更新到value中*/public final int incrementAndGet() {return unsafe.getAndAddInt(this, valueOffset, 1) + 1;}/*** 先对成员变量value减1,然后把修改后的值更新到value中*/public final int decrementAndGet() {return unsafe.getAndAddInt(this, valueOffset, -1) - 1;}/*** 先对成员变量value增加delta,然后把修改后的值更新到value中*/public final int addAndGet(int delta) {return unsafe.getAndAddInt(this, valueOffset, delta) + delta;}/*** 先获取value的值赋值给prev,然后对prev执行功能函数updateFunction,把结果next更新到value中*//*该方法用到了自旋锁,当prev的值等于内存中的value值时,才会把next的值更新到value中,否则该方法会进行自旋,即一遍遍的获取锁重试,当prev的值等于内存中的value值时进行更新*/public final int getAndUpdate(IntUnaryOperator updateFunction) {int prev, next;do {prev = get();next = updateFunction.applyAsInt(prev);} while (!compareAndSet(prev, next));return prev;}/*** 该方法与上面方法原理相同,只是先更新后,再获取value值*/public final int updateAndGet(IntUnaryOperator updateFunction) {int prev, next;do {prev = get();next = updateFunction.applyAsInt(prev);} while (!compareAndSet(prev, next));return next;}/*** 该方法与上面方法原理相同,只是功能函数变为了IntBinaryOperator,执行两个整数返回一个整数;* 先获取value值,然后功能函数执行后的结果更新到value中*/public final int getAndAccumulate(int x,IntBinaryOperator accumulatorFunction) {int prev, next;do {prev = get();next = accumulatorFunction.applyAsInt(prev, x);} while (!compareAndSet(prev, next));return prev;}/*** 该方法与上面函数类似,只是先更新value值,然后获取返回更新后的value值*/public final int accumulateAndGet(int x,IntBinaryOperator accumulatorFunction) {int prev, next;do {prev = get();next = accumulatorFunction.applyAsInt(prev, x);} while (!compareAndSet(prev, next));return next;}/*** 把value值转化为String 类型*/public String toString() {return Integer.toString(get());}/***获取value成员变量值*/public int intValue() {return get();}/*** 把value值转化为long类型*/public long longValue() {return (long)get();}/*** 把value值转化为float类型*/public float floatValue() {return (float)get();}/*** 把value值转化为double类型*/public double doubleValue() {return (double)get();}}

3. AtomicInteger演示示例

package com.lzj.atomic;import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.IntBinaryOperator;
import java.util.function.IntUnaryOperator;public class AtomicIntegerDemo2 {public static void main(String[] args) {get();set();getAndSet();getAndIncrement();getAndDecrement();getAndAdd();incrementAndGet();decrementAndGet();addAndGet();getAndUpdate();updateAndGet();getAndAccumulate();accumulateAndGet();}/*测试AtomicInterger.get方法*/public static void get() {AtomicInteger at = new AtomicInteger(10);int value = at.get();System.out.println("get() : " + value);}/*测试AtomicInteger.get方法*/public static void set() {AtomicInteger at = new AtomicInteger(10);at.set(20);System.out.println("set()" + at.get());}/*测试AtomicInteger.getAndSet*/public static void getAndSet() {AtomicInteger at = new AtomicInteger(10);int oldValue = at.getAndSet(20);System.out.println("getAndSet : " + at.get());System.out.println("getAndSet : " + oldValue);}/*测试AtomicInteger.getAndIncrement*/public static void getAndIncrement() {AtomicInteger at = new AtomicInteger(10);at.getAndIncrement();System.out.println("getAndIncrement : " + at.get());}public static void getAndDecrement() {AtomicInteger at = new AtomicInteger(10);at.getAndDecrement();System.out.println("getAndDecrement : " + at.get());}/*测试AtomicInteger.getAndAdd*/public static void getAndAdd() {AtomicInteger at = new AtomicInteger(10);at.getAndAdd(5);System.out.println("getAndAdd : " + at.get());}/*测试AtomicInteger.incrementAndGet*/public static void incrementAndGet() {AtomicInteger at = new AtomicInteger(10);int current = at.incrementAndGet();System.out.println("incrementAndGet() : " + at.get());System.out.println("incrementAndGet() : " + current);}/*AtomicInteger.decrementAndGet*/public static void decrementAndGet() {AtomicInteger at = new AtomicInteger(10);int current = at.decrementAndGet();System.out.println("decrementAndGet() : " + at.get());System.out.println("decrementAndGet() : " + current);}/*测试AtomicInteger.addAndGet*/public static void addAndGet() {AtomicInteger at = new AtomicInteger(10);int current = at.addAndGet(10);System.out.println("addAndGet() : " + at.get());System.out.println("current : " + current);}/*测试AtomicInteger.getAndUpdate*/public static void getAndUpdate() {AtomicInteger at = new AtomicInteger(11);IntUnaryOperator updateFunction = x -> x/2;int old = at.getAndUpdate(updateFunction);System.out.println("getAndUpdate() : " + at.get());System.out.println("getAndUpdate() : " + old);}/*测试AtomicInteger.updateAndGet*/public static void updateAndGet() {AtomicInteger at = new AtomicInteger(11);IntUnaryOperator updateFunction = x -> x/2;int current = at.updateAndGet(updateFunction);System.out.println("updateAndGet() : " + at.get());System.out.println("updateAndGet() : " + current);}/*测试AtomicInteger。getAndAccumulate*/public static void getAndAccumulate() {AtomicInteger at = new AtomicInteger(11);IntBinaryOperator accumulatorFunction = (x,y) -> x / y;int old = at.getAndAccumulate(2, accumulatorFunction);System.out.println("getAndAccumulate() : " + at.get());System.out.println("getAndAccumulate() : " + old);}/*测试AtomicInteger.accumulateAndGet*/public static void accumulateAndGet() {AtomicInteger at = new AtomicInteger(11);IntBinaryOperator accumulatorFunction = (x,y) -> x / y;int current = at.accumulateAndGet(2, accumulatorFunction);System.out.println("accumulateAndGet() : " + at.get());System.out.println("accumulateAndGet() : " + current);}
}

执行结果如下

get() : 10
set()20
getAndSet : 20
getAndSet : 10
getAndIncrement : 11
getAndDecrement : 9
getAndAdd : 15
incrementAndGet() : 11
incrementAndGet() : 11
decrementAndGet() : 9
decrementAndGet() : 9
addAndGet() : 20
current : 20
getAndUpdate() : 5
getAndUpdate() : 11
updateAndGet() : 5
updateAndGet() : 5
getAndAccumulate() : 5
getAndAccumulate() : 11
accumulateAndGet() : 5
accumulateAndGet() : 5

4. 多线程用法

package com.lzj.atomic;import java.util.concurrent.atomic.AtomicInteger;public class AtomicDemo1 {public static AtomicInteger count = new AtomicInteger(10);public void add() {count.getAndIncrement();System.out.println("thread1 : " + count);}public void dec() {System.out.println("thread2 : " + count);count.getAndDecrement();}public static void main(String[] args) {AtomicDemo1  atomicDemo1 = new AtomicDemo1();Runnable run1 = new Runnable() {public void run() {atomicDemo1.add();System.out.println("thread1 : " + atomicDemo1.count);}};Runnable run2 = new Runnable() {public void run() {atomicDemo1.dec();System.out.println("thread2 : " + atomicDemo1.count);}};Thread thread1 = new Thread(run1);Thread thread2 = new Thread(run2);thread1.start();thread2.start();}
}

另外,AtomicLong与AtomicInteger除了value的类型不一样,其它几乎都类似。
AtomicStampedReference与AtomicInteger源码基本类似,只是AtomicInteger用来处理整数类型,通过自旋锁的方式利用compareAndSwapInt来解决CAS问题;而AtomicStampedReference用来处理对象,通过自旋锁的方式利用compareAndSwapObject来解决CAS问题,关于AtomicStampedReference的详解参考AtomicStampedReference

这篇关于并发读源码——AtomicInteger/AtomicLong/AtomicStampedReference的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/994179

相关文章

Go中sync.Once源码的深度讲解

《Go中sync.Once源码的深度讲解》sync.Once是Go语言标准库中的一个同步原语,用于确保某个操作只执行一次,本文将从源码出发为大家详细介绍一下sync.Once的具体使用,x希望对大家有... 目录概念简单示例源码解读总结概念sync.Once是Go语言标准库中的一个同步原语,用于确保某个操

Java汇编源码如何查看环境搭建

《Java汇编源码如何查看环境搭建》:本文主要介绍如何在IntelliJIDEA开发环境中搭建字节码和汇编环境,以便更好地进行代码调优和JVM学习,首先,介绍了如何配置IntelliJIDEA以方... 目录一、简介二、在IDEA开发环境中搭建汇编环境2.1 在IDEA中搭建字节码查看环境2.1.1 搭建步

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟 开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚 第一站:海量资源,应有尽有 走进“智听

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

高并发环境中保持幂等性

在高并发环境中保持幂等性是一项重要的挑战。幂等性指的是无论操作执行多少次,其效果都是相同的。确保操作的幂等性可以避免重复执行带来的副作用。以下是一些保持幂等性的常用方法: 唯一标识符: 请求唯一标识:在每次请求中引入唯一标识符(如 UUID 或者生成的唯一 ID),在处理请求时,系统可以检查这个标识符是否已经处理过,如果是,则忽略重复请求。幂等键(Idempotency Key):客户端在每次

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

音视频入门基础:WAV专题(10)——FFmpeg源码中计算WAV音频文件每个packet的pts、dts的实现

一、引言 从文章《音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道,通过FFprobe命令可以打印WAV音频文件每个packet(也称为数据包或多媒体包)的信息,这些信息包含该packet的pts、dts: 打印出来的“pts”实际是AVPacket结构体中的成员变量pts,是以AVStream->time_base为单位的显

kubelet组件的启动流程源码分析

概述 摘要: 本文将总结kubelet的作用以及原理,在有一定基础认识的前提下,通过阅读kubelet源码,对kubelet组件的启动流程进行分析。 正文 kubelet的作用 这里对kubelet的作用做一个简单总结。 节点管理 节点的注册 节点状态更新 容器管理(pod生命周期管理) 监听apiserver的容器事件 容器的创建、删除(CRI) 容器的网络的创建与删除