J.U.C 原子类之AtomicIntegerFieldUpdate

news2024/9/25 13:23:27
❃博主首页 : 「码到三十五」 ,同名公众号 :「码到三十五」,wx号 : 「liwu0213」
☠博主专栏 : <mysql高手> <elasticsearch高手> <源码解读> <java核心> <面试攻关>
♝博主的话 : 搬的每块砖,皆为峰峦之基;公众号搜索「码到三十五」关注这个爱发技术干货的coder,一起筑基

在Java的并发编程中,AtomicIntegerFieldUpdater是一个非常重要的工具类,它提供了一种线程安全的方式来更新某个类的指定volatile int字段,而无需使用同步锁。

文章目录

      • 一、AtomicIntegerFieldUpdater简介
      • 二、AtomicIntegerFieldUpdater与AtomicInteger区别
        • 1. 使用场景
        • 2. 内存占用
        • 3. 实现机制
      • 二、使用AtomicIntegerFieldUpdater的步骤
      • 三、AtomicIntegerFieldUpdater的优势
      • AtomicIntegerFieldUpdater使用限制
      • 四、实际应用场景
      • 五、使用

一、AtomicIntegerFieldUpdater简介

AtomicIntegerFieldUpdater是Java并发包java.util.concurrent.atomic中的一个类,它利用反射机制,在不创建额外对象的情况下,能够原子地更新某个类的指定volatile int字段。这种机制特别适用于那些实例数量非常多,且每个实例都需要原子更新某个字段的场景,因为它可以显著减少内存占用并提高性能。

二、AtomicIntegerFieldUpdater与AtomicInteger区别

AtomicInteger可以保证内部的属性的操作时原子性的;AtomicIntegerFieldUpdater是保证其他类的属性的操作时原子性的。当一个类新建时,可以选型Atomic类型的原子类,但当对已经存在的一个类,如要保证内部的操作为原子性,就要借助AtomicIntegerFieldUpdater了。

但是AtomicIntegerFieldUpdater也有它的局限性,它处理的类中的属性必须保证是int类型的(不能是Integer包装类型)、必须是volatile类型的,否则不能用AtomicIntegerFieldUpdater来保证其原子性。

它们之间的主要区别还体现在使用场景、内存占用、以及实现机制上。

1. 使用场景
  • AtomicInteger:适用于单个变量需要原子性操作的场景。当你需要在多线程环境下安全地增加或减少一个整数值时,AtomicInteger是一个很好的选择。它封装了一个volatile int变量,并提供了多种原子操作方法,如incrementAndGet()decrementAndGet()等。
  • AtomicIntegerFieldUpdater:适用于类的实例数量较多,且每个实例都需要原子性地更新某个volatile int字段的场景。通过反射机制,AtomicIntegerFieldUpdater可以在不修改原有类结构的情况下,为类的指定字段提供原子更新能力。这种方式减少了为每个实例创建AtomicInteger对象的内存消耗,提高了性能。
2. 内存占用
  • AtomicInteger:每个需要原子性操作的变量都会创建一个AtomicInteger对象,因此在实例数量较多时,会占用较多的内存。
  • AtomicIntegerFieldUpdater:不需要为每个实例创建AtomicIntegerFieldUpdater对象,而是创建一个静态的AtomicIntegerFieldUpdater实例来更新所有实例的指定字段。这种方式显著减少了内存占用。
3. 实现机制
  • AtomicInteger:内部封装了一个volatile int变量,并通过CAS(Compare-And-Swap)操作来保证原子性。CAS是一种基于硬件的原子指令,它可以在不锁定对象的情况下,实现多个线程对同一个变量的安全访问和修改。
  • AtomicIntegerFieldUpdater:通过反射机制获取到类的指定字段的Field对象,然后利用sun.misc.Unsafe类的底层操作来实现CAS操作。Unsafe类提供了低级别的、非安全的、操作系统级别的访问方法,它可以直接访问内存、创建对象、数组等。由于AtomicIntegerFieldUpdater是基于反射和Unsafe类实现的,因此它只能更新公共字段或具有公共setter方法的字段。

二、使用AtomicIntegerFieldUpdater的步骤

  1. 字段声明
    被更新的字段必须是volatile int类型,这是使用AtomicIntegerFieldUpdater的前提条件。

  2. 创建Updater
    使用AtomicIntegerFieldUpdater.newUpdater()静态方法创建一个AtomicIntegerFieldUpdater实例。这个方法需要两个参数:一个是包含要更新字段的类的Class对象,另一个是字段的名称(String类型)。

  3. 原子更新
    通过Updater实例提供的方法(如compareAndSetgetAndIncrementgetAndSet等)进行原子更新操作。这些方法允许你在不锁定对象的情况下安全地更新字段。

三、AtomicIntegerFieldUpdater的优势

  1. 减少内存占用
    使用AtomicIntegerFieldUpdater可以避免为每个需要原子更新的字段创建一个AtomicInteger对象,从而显著减少内存占用。这在处理大量实例时尤为重要。

  2. 提高性能
    由于AtomicIntegerFieldUpdater直接操作底层字段,避免了通过方法调用间接访问字段的开销,因此在某些场景下,其性能可能优于使用AtomicInteger

  3. 灵活性
    AtomicIntegerFieldUpdater允许开发者在不改变现有类结构的情况下,为类中的某个字段提供原子更新能力。这种灵活性使得它非常适用于遗留代码的改造和优化。

AtomicIntegerFieldUpdater使用限制

(1)字段必须是volatile类型的,在线程之间共享变量时保证立即可见.eg:volatile int value = 3

(2)字段的描述类型(修饰符public/protected/default/private)是与调用者与操作对象字段的关系一致。也就是说调用者能够直接操作对象字段,那么就可以反射进行原子操作。但是对于父类的字段,子类是不能直接操作的,尽管子类可以访问父类的字段。

(3)只能是实例变量,不能是类变量,也就是说不能加static关键字。

(4)只能是可修改变量,不能使final变量,因为final的语义就是不可修改。实际上final的语义和volatile是有冲突的,这两个关键字不能同时存在。

(5)对于AtomicIntegerFieldUpdater和AtomicLongFieldUpdater只能修改int/long类型的字段,不能修改其包装类型(Integer/Long)。如果要修改包装类型就需要使用AtomicReferenceFieldUpdater。

四、实际应用场景

  1. 缓冲区引用计数更新
    在Netty等网络编程框架中,缓冲区(如ByteBuf)通常使用引用计数来管理内存。由于缓冲区实例的数量可能非常大,使用AtomicIntegerFieldUpdater来更新引用计数可以避免为每个缓冲区实例创建一个AtomicInteger对象,从而显著减少内存占用。

  2. 并发数据结构
    在实现并发数据结构(如并发队列、并发哈希表等)时,AtomicIntegerFieldUpdater可以用于原子地更新数据结构的某些状态字段(如计数器、标记位等)。

  3. 性能监控
    在需要对系统的某些性能指标进行原子更新时(如请求计数器、活跃用户数等),AtomicIntegerFieldUpdater可以提供一个高效且线程安全的更新机制。

五、使用

假设有一个银行账户类BankAccount,其中包含一个volatile int类型的余额字段balance。我们希望在多线程环境中,能够安全地对这个余额字段进行增加操作,以确保在并发转账时余额的准确性。

首先,我们定义BankAccount类,并使用AtomicIntegerFieldUpdater来更新余额字段。

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

public class BankAccount {
    // 使用volatile修饰,保证内存可见性
    private volatile int balance;

    // 静态的AtomicIntegerFieldUpdater实例,用于更新balance字段
    private static final AtomicIntegerFieldUpdater<BankAccount> balanceUpdater =
            AtomicIntegerFieldUpdater.newUpdater(BankAccount.class, "balance");

    public BankAccount(int initialBalance) {
        this.balance = initialBalance;
    }

    // 使用AtomicIntegerFieldUpdater增加余额
    public void deposit(int amount) {
        balanceUpdater.addAndGet(this, amount);
    }

    // 获取当前余额
    public int getBalance() {
        return balance;
    }
}

接下来,我们创建一个多线程测试类来模拟多个账户同时存款的情况。

public class BankAccountTest {

    public static void main(String[] args) {
        // 创建一个账户,初始余额为100
        BankAccount account = new BankAccount(100);

        // 创建多个线程来模拟存款操作
        int numThreads = 10;
        Thread[] threads = new Thread[numThreads];
        for (int i = 0; i < numThreads; i++) {
            final int amount = 50; // 每个线程存款50
            threads[i] = new Thread(() -> {
                account.deposit(amount);
                System.out.println("Thread " + Thread.currentThread().getName() + " deposited. New balance: " + account.getBalance());
            });
            threads[i].start();
        }

        // 等待所有线程完成
        for (Thread t : threads) {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 输出最终余额
        System.out.println("Final balance: " + account.getBalance());
    }
}

关注公众号[码到三十五]获取更多技术干货 !

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1975841.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL:数据库用户

数据库用户 在关系型数据库管理系统中&#xff0c;数据库用户&#xff08;USER&#xff09;是指具有特定权限和访问权限的登录账户。每个用户都有自己的用户名和密码&#xff0c;以便系统可以通过认证来识别他们的身份。数据库用户可以登录数据库&#xff0c;在其中执行各种类…

第二十天学习笔记2024.8.2

安装mysql 1.安装软件包 centos7 中安装 mysql 8.x_wffkg-CSDN博客 2.解压 tar -xf mysql-8.0.33-1.el7.x86_64.rpm-bundle.tar 3.卸载mariadb yum remove -y *mariadb* 4.安装&#xff08;缺什么依赖补什么&#xff09; mysql-community-server-8.0.33-1.el7.x86_64.rpm 5.…

APP逆向 day26unidbg下-pdd(anti)案例

一.前言 今天我们讲unidbg的下篇&#xff0c;也就是unidbg基础的最后一个部分&#xff0c;我们上节课也有补环境&#xff0c;比如补java环境&#xff0c;补安卓环境&#xff0c;这节课我们讲的肯定比这些都要难&#xff0c;我会给出一个之前讲过的案例&#xff0c;然后会讲一个…

如何建立与众不同的市场洞察能力【深度】

来源&#xff1a;战略研发领航 建立与众不同的市场洞察机制&#xff0c;展示了如何在组织中建立一种以数据和洞察为核心的文化&#xff0c;并通过4S周期&#xff08;架构、探查、塑造、成型&#xff09;的洞察工作方法论&#xff0c;指导领导者和团队在各个层级上进行更有效的思…

【C语言】C语言期末突击/考研--函数

目录 一、函数的声明与定义-嵌套调用 1.1.函数的声明与定义 1.2.函数的分类与调用 二、函数的递归调用 三、局部变量与全局变量 3.1.全局变量解析形参实参解析 3.2.局部变量与全局变量 四、练习题及解析 一、函数的声明与定义-嵌套调用 1.1.函数的声明与定义 函数间的…

Code Review 这件事,AI 比人类强 10 倍

1.引言 代码审查&#xff0c;也就是 Code Review&#xff08;以下简称 CR&#xff09;&#xff0c;一直是 IT 行业讨论的热门话题&#xff0c;因为它在很大程度上决定了公司的工程质量。 但是&#xff0c;人工 CR 的成本的是很高的&#xff0c;所以在实际落地场景&#xff0c…

latex换行\left[和\right]编译报错-解决方案

简而言之&#xff1a;\\ 换成 \right.\\ , & 换成 &\left. 来个例子就知道了&#xff1a; 原本的公式是&#xff1a; \begin{align}\label{up_critic} L_Q(\theta) & \mathbb{E}\left[\frac{1}{2}(Q_\theta(\mathcal{S}_{k,t}^m, {A}_{k,t}^m) - ({R}_{k,t}^m …

07030405复杂可编程逻辑器件CPLD现场可编程阵列FPGA

复杂可编程逻辑器件CPLD&现场可编程阵列FPGA 7.3 复杂可编程逻辑器件CPLD7.3.1CPLD的结构 7.4现场可编程门阵列FPGA7.4.1FPGA实现逻辑功能的基本原理7.4.2FPGA结构简介1.可编程逻辑块2.I/O块3.可编程连线资源CPLD与FPGA的区别 7.5可编程逻辑器件开发过程简介编程条件 7.3 复…

细说文件操作

你好&#xff01;感谢支持孔乙己的新作&#xff0c;本文就结构体与大家分析我的思路。 希望能大佬们多多纠正及支持 &#xff01;&#xff01;&#xff01; 个人主页&#xff1a;爱摸鱼的孔乙己-CSDN博客 目录 1.什么是文件 1.1.程序设计文件 1.1.1.程序文件 1.1.2.数据文件…

使用EntityFramework8的学习和开发过程中一些经验

前言&#xff1a; 本篇文章主要记录自己在EF8的学习和使用过程中的碰到一些坑和自己琢磨的一些解决问题的方法以及过程。 问题汇总&#xff1a; 一、Code First 模式下生成表中字段的没有按照我们想要的次序排列 实体上增加注解 [Column(Order 0)] 可以通过Order来控制字段…

[CISCN2019 华北赛区 Day1 Web1]Dropbox 1

目录 题目分析功能测试代码读取index.phpdownload.phpdelete.phpclass.php 关键代码审计user类FileList类File类思路 解题步骤php脚本解题 题目分析 功能测试 注册登录后来到上传文件界面&#xff0c;通过改后缀&#xff0c;改文件头&#xff0c;改content-type&#xff0c;上…

如何养护我们的头发一

正常头发含水量10-13% 头发含水量低 头发就会干燥 受到损伤 头发保水能力弱 保养头发,最重要的是防止头发头发的流水. 正确的洗涤方法 干性头发,一般3天洗一次 油性头发,一般1天洗一次 中性头发,一般2天洗一次 (2)正确的洗发步骤 选择好梳子建议选用木梳或牛角梳 按摩头皮…

释疑 803-(2)物理层 整理总结

目录 2-01 物理层要解决哪些问题?物理层的主要特点是什么? 2-02 规程与协议有什么区别? 2-03 试给出数据通信系统的模型并说明其主要组成构件的作用。 2-04 试解释以下名词&#xff1a;数据、信号、模拟数据、模拟信号、基带信号、带通信号、数字数据、数字信号、码元、…

PHP智能问诊导诊平台-计算机毕业设计源码75056

摘 要 智能问诊导诊平台作为一种智能化医疗服务工具&#xff0c;利用PHP语言开发&#xff0c;旨在为用户提供便捷的在线问诊和导诊服务。该平台集成了智能算法和医疗数据&#xff0c;实现了智能化的病情诊断和治疗建议&#xff0c;帮助用户更快速地获取医疗信息和建议。用户可…

未授权访问

一、Redis 未授权访问 redis-cli -h 192.168.4.176-p6379 &#xff08;使用这个连接&#xff09; 连接成功后使用info测试 二、MongDB 未授权访问 使用fofa搜mongdb端口复制ip到Navicat,连接 】 三、Zookeeper未授权访问漏洞 使用fofa 搜索2181端口使用下面命令在kali在…

5、SystemC行为级建模

1、sc_port关联sc_interface和channel。sc_module聚合sc_module、var、process和sc_port。sc_cthread和sc_method继承sc_thread。 sc_interface中声明很多虚函数&#xff0c;作为接口函数实现接口功能&#xff0c;如send()、recv()等。可以通过发送和接收模块的端口调用(sc_in…

CCF编程能力等级认证GESP—C++3级—20240629

CCF编程能力等级认证GESP—C3级—20240629 单选题&#xff08;每题 2 分&#xff0c;共 30 分&#xff09;判断题&#xff08;每题 2 分&#xff0c;共 20 分&#xff09;编程题 (每题 25 分&#xff0c;共 50 分)移位寻找倍数 单选题&#xff08;每题 2 分&#xff0c;共 30 分…

【基础篇】MySQL数据库详解:基础知识详解

一、SQL分类 1.DDL2.DML3.DQL4.DCL二、函数 1.字符串函数2.数值函数3.日期函数4.流程函数三、约束 1.概述2.约束演示3.外键约束四、多表查询 1.多表关系2.多表查询表述3.内连接4.外连接5.自连接6.子查询五、事务 1.事务简介2.事务操作3.事务四大特性4.并发事务问题5.事务隔离级…

C语言——求阶乘的两种方法

第一种方法使用了递归思想 #include <stdio.h> int fun(int N) {if (N 0){return 1;}else{return (fun(N - 1) * N);} } int main() {int N 0;scanf_s("%d", &N);printf("%d",fun(N)); } 第二种方法用的for循环 #include <stdio.h> i…

如何开启idea中的断言功能?

目录 一、什么是断言&#xff1f; 二、Java断言的语法 三、开启断言 一、什么是断言&#xff1f; 断言&#xff08;assert&#xff09;是 Java 中的一条语句&#xff0c;一种在程序中的逻辑&#xff08;如一个结果为真或假的逻辑判断式&#xff09;&#xff0c;目的是验证软…