原子操作的基本数据类型


基本类型的原子操作主要有这些:

  1. AtomicBoolean:以原子更新的方式更新 boolean;
  2. AtomicInteger:以原子更新的方式更新 Integer;
  3. AtomicLong:以原子更新的方式更新 Long;

这几个类的用法基本一致,这里以 AtomicInteger 为例。

  1. addAndGet(int delta) :增加给定的 delta,并获取新值。
  2. incrementAndGet():增加 1,并获取新值。
  3. getAndSet(int newValue):获取当前值,并将新值设置为 newValue。
  4. getAndIncrement():获取当前值,并增加 1。

AtomicInteger 的 getAndIncrement 方法:

public final int getAndIncrement() {
// 使用Unsafe类中的getAndAddInt方法原子地增加AtomicInteger的当前值
// 第一个参数this是AtomicInteger的当前实例
// 第二个参数valueOffset是一个偏移量,它指示在AtomicInteger对象中的哪个位置可以找到实际的int值
// 第三个参数1表示要加到当前值上的值(即增加的值)
// 此方法返回的是增加前的原始值
return unsafe.getAndAddInt(this, valueOffset, 1);
}

Unsafe 类是 Java 中的一个特殊类,用于执行低级、不安全的操作。getAndIncrement 方法就是利用了 Unsafe 类提供的 CAS(Compare-And-Swap)操作来实现原子的 increment 操作。CAS 是一种常用的无锁技术,允许在多线程环境中原子地更新值。

示例:

public class Main {
private static final AtomicInteger atomicInteger = new AtomicInteger(1); public static void main(String[] args) {
System.out.println(atomicInteger.getAndIncrement());
System.out.println(atomicInteger.get());
}
}

AtomicBoolean 类的 compareAndSet 方法:

public final boolean compareAndSet(boolean expect, boolean update) {
// 将expect布尔值转化为整数,true为1,false为0
int e = expect ? 1 : 0; // 将update布尔值转化为整数,true为1,false为0
int u = update ? 1 : 0; // 使用Unsafe类中的compareAndSwapInt方法尝试原子地更新AtomicBoolean的当前值
// 第一个参数this是AtomicBoolean的当前实例
// 第二个参数valueOffset是一个偏移量,它指示在AtomicBoolean对象中的哪个位置可以找到实际的int值
// 第三个参数e是我们期望的当前值(转换为整数后的值)
// 第四个参数u是我们想要更新的值(转换为整数后的值)
// 如果当前值与期望值e相等,它会被原子地设置为u,并返回true;否则返回false。
return unsafe.compareAndSwapInt(this, valueOffset, e, u);
}

原子操作的数组类型


如果需要原子更新数组里的某个元素,atomic 也提供了相应的类:

  1. AtomicIntegerArray:这个类提供了一些原子更新 int 整数数组的方法。
  2. AtomicLongArray:这个类提供了一些原子更新 long 型证书数组的方法。
  3. AtomicReferenceArray:这个类提供了一些原子更新引用类型数组的方法。

这几个类的用法一致,就以 AtomicIntegerArray 来总结下常用的方法:

  1. addAndGet(int i, int delta):以原子更新的方式将数组中索引为 i 的元素与输入值相加;
  2. getAndIncrement(int i):以原子更新的方式将数组中索引为 i 的元素自增加 1;
  3. compareAndSet(int i, int expect, int update):将数组中索引为 i 的位置的元素进行更新

示例:

public class Main {
private static final int[] value = new int[]{1, 2, 3};
private static final AtomicIntegerArray integerArray = new AtomicIntegerArray(value); public static void main(String[] args) {
// 对数组中索引为1的位置的元素加5
int result = integerArray.getAndAdd(1, 5);
System.out.println(integerArray.get(1));
System.out.println(result);
}
}

原子操作的引用类型


如果需要原子更新引用类型的话,atomic 也提供了相关的类:

  1. AtomicReference:原子更新引用类型;
  2. AtomicReferenceFieldUpdater:原子更新引用类型里的字段;
  3. AtomicMarkableReference:原子更新带有标记位的引用类型;

示例:

public class Main {
private static final AtomicReference<User> reference = new AtomicReference<>(); public static void main(String[] args) {
User user1 = new User("a", 1);
reference.set(user1);
User user2 = new User("b", 2);
User user = reference.getAndSet(user2);
System.out.println(user);
System.out.println(reference.get());
} static class User {
private final String userName;
private final int age; public User(String userName, int age) {
this.userName = userName;
this.age = age;
} @Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", age=" + age +
'}';
}
}
}

原子更新字段类型


如果需要更新对象的某个字段,atomic 同样也提供了相应的原子操作类:

  1. AtomicIntegeFieldUpdater:原子更新整型字段类;
  2. AtomicLongFieldUpdater:原子更新长整型字段类;
  3. AtomicStampedReference:原子更新引用类型,这种更新方式会带有版本号,是为了解决 CAS 的 ABA 问题

使用原子更新字段需要两步:

  1. 通过静态方法newUpdater创建一个更新器,并且设置想要更新的类和字段;
  2. 字段必须使用public volatile进行修饰;

示例:

public class Main {
private static final AtomicIntegerFieldUpdater<User> updater = AtomicIntegerFieldUpdater.newUpdater(User.class, "age"); public static void main(String[] args) {
User user = new User("a", 1);
int oldValue = updater.getAndAdd(user, 5);
System.out.println(oldValue);
System.out.println(updater.get(user));
} static class User {
private final String userName;
public volatile int age; public User(String userName, int age) {
this.userName = userName;
this.age = age;
} @Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", age=" + age +
'}';
}
}
}

原子操作类Atomic的更多相关文章

  1. Java 并发系列之九:java 原子操作类Atomic(13个)

    1. 原子更新基本类型类 2. 原子更新数组 3. 原子更新引用 4. 原子更新属性 5. txt java 原子操作类Atomic 概述 java.util.concurrent.atomic里的原 ...

  2. 并发之java.util.concurrent.atomic原子操作类包

    15.JDK1.8的Java.util.concurrent.atomic包小结 14.Java中Atomic包的原理和分析 13.java.util.concurrent.atomic原子操作类包 ...

  3. 24.Java中atomic包中的原子操作类总结

    1. 原子操作类介绍 在并发编程中很容易出现并发安全的问题,有一个很简单的例子就是多线程更新变量i=1,比如多个线程执行i++操作,就有可能获取不到正确的值,而这个问题,最常用的方法是通过Synchr ...

  4. java架构之路(多线程)原子操作,Atomic与Unsafe魔术类

    这次不讲原理了,主要是一些应用方面的知识,和上几次的JUC并发编程的知识点更容易理解. 知识回顾: 上次主要说了Semaphore信号量的使用,就是一个票据的使用,我们举例了看3D电影拿3D眼镜的例子 ...

  5. 原子操作(atomic operation)

    深入分析Volatile的实现原理 引言 在多线程并发编程中synchronized和Volatile都扮演着重要的角色,Volatile是轻量级的synchronized,它在多处理器开发中保证了共 ...

  6. java并发编程基础 --- 7章节 java中的13个原子操作类

    当程序更新一个变量时,如果多线程同时更新这个变量,可能得到期望之外的值,比如变量 i=1,A线程更新 i+1,B线程也更新 I+1,经过两个线程的操作之后可能 I不等于3,而是等于2.因为A和B线程更 ...

  7. Java原子操作类AtomicInteger应用场景

    Java中有那么一些类,是以Atomic开头的.这一系列的类我们称之为原子操作类.以最简单的类AtomicInteger为例.它相当于一个int变量,我们执行Int的 i++ 的时候并不是一个原子操作 ...

  8. JAVA 原子操作类

    上文中,guava代码中就用到了,在这里再专门捋一下 部分内容源自: https://www.jianshu.com/p/712681f5aecd https://www.yiibai.com/jav ...

  9. Java原子操作类汇总

    当程序更新一个变量时,如果是多线程同时更新这个变量,可能得到的结果与期望值不同.比如:有一个变量i,A线程执行i+1,B线程也执行i+1,经过两个线程的操作后,变量i的值可能不是期望的3,而是2.这是 ...

  10. 原子操作类AtomicInteger详解

    为什么需要AtomicInteger原子操作类?对于Java中的运算操作,例如自增或自减,若没有进行额外的同步操作,在多线程环境下就是线程不安全的.num++解析为num=num+1,明显,这个操作不 ...

随机推荐

  1. Temperature 题解

    前言 题目链接:洛谷:SPOJ:Hydro & bzoj. 题意简述 有一个长度为 \(n\) 的序列,每个位置值的范围为 \([L_i, R_i]\) 内,求原序列可能的最长不降子串长度. ...

  2. mysql8.0 主从架构模式【0到1架构系列】

    前提条件 准备3,4,5台虚拟机 祼装mysql8.0 主从架构 常见两种模式"一主多从"和"级联复制"两种,基本都很简单,都是依赖binlog日志文件进行同步 ...

  3. 22张图详解浏览器请求数据包如何到达web服务器(搞懂网络可以毕业了)

    浏览器的请求数据包如何到达web服务器? 很多读者对于其中的完整流程不是特别的了解,下面一口君通过这22张图,详细的讲解我们点击浏览器的网址之后,数据包是如何经过重重险阻到达web server的. ...

  4. 手把手教Linux驱动10-platform总线详解

    platform总线是学习linux驱动必须要掌握的一个知识点. 本文参考已发布:Linux 3.14内核 一.概念 嵌入式系统中有很多的物理总线:I2c.SPI.USB.uart.PCIE.APB. ...

  5. 零基础学习人工智能—Python—Pytorch学习(五)

    前言 上文有一些文字打错了,已经进行了修正. 本文主要介绍训练模型和使用模型预测数据,本文使用了一些numpy与tensor的转换,忘记的可以第二课的基础一起看. 线性回归 结合numpy使用 首先使 ...

  6. 前端使用 Konva 实现可视化设计器(21)- 绘制图形(椭圆)

    本章开始补充一些基础的图形绘制,比如绘制:直线.曲线.圆/椭形.矩形.这一章主要分享一下本示例是如何开始绘制一个图形的,并以绘制圆/椭形为实现目标. 请大家动动小手,给我一个免费的 Star 吧~ 大 ...

  7. .NET Core学习笔记(6)——UWP略过SSL证书调用SignalR服务

    在前一篇<.NET Core学习笔记(5)--WebAPI从Server端push消息到Client>中,我们简单学习了.NET Core版本SignalR的使用.Sample工程里我们创 ...

  8. maven 插件之 maven-shade-plugin,解决同包同名 class 共存问题的神器

    开心一刻 有一天螃蟹出门,不小心撞倒了泥鳅泥鳅很生气地说:你是不是瞎啊!螃蟹说:不是啊,我是螃蟹 概述 maven-shade-plugin 官网已经介绍的很详细了,我给大家简单翻译一下 This p ...

  9. 同步多个mysql 到一个

    了解大概 Ref: is it possible that canal set with multiple mysql database source 使用 canal https://dev.mys ...

  10. 在 Web 中判断页面是不是刷新

    在 Web 开发中,我们经常需要区分用户是否通过刷新操作重新加载了页面.这一操作可能是由用户手动刷新(如按下 F5 键或点击浏览器刷新按钮)或通过浏览器自动重新加载.判断页面是否刷新有助于开发者优化用 ...