2024年【并发编程系列11】Java中12个原子(Atomic)操作类实现原理分析,netty框架教程

最后

终极手撕架构师的学习笔记:分布式+微服务+开源框架+性能优化

image

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

    • AtomicIntegerFieldUpdater
    • 代码示例
  • AtomicLongFieldUpdater

  • AtomicReferenceFieldUpdater

    • 示例
  • CAS操作ABA问题

  • 总结

前言

===============================================================

我们知道i++操作实际上是线程不安全的,因为一个i++操作分为了三步:

  • 1、获取的i的值

  • 2、执行i+1

  • 3、将i+1的结果赋值给i

而这三步不是一个原子操作,多线程环境下就会出现线程不安全性问题。

Java从JDK 1.5开始,在java.util.concurrent.atomic包下提供了12个对应的原子类操作,让我们可以直接使用原子操作类来实现一个原子的i++操作。

Java中一共提供了12个原子类操作,可以分为四种类型,分别是:

  • 原子更新基本类型

  • 原子更新数组

  • 原子更新引用

  • 原子更新属性

下面就让我们一起来看看这四大类中的12个原子操作类:

原子更新基本类型

=====================================================================

原子更新基本数据类型有以下三个:

  • AtomicInteger:原子更新整型。

  • AtomicBoolean:原子更新布尔类型。

  • AtomicLong:原子更新长整型。

AtomicInteger


常用方法如下:

  • int addAndGet(int delta):

以原子方式将传入的数值与实例中的值(AtomicInteger里的 value)相加,并返回结果。

  • boolean compareAndSet(int expect,int update):

如果输入的数值等于预期值,则以原子方式将该值设置为输入的值,成功返回true,替换失败则返回false

  • int getAndIncrement():

以原子方式将当前值自增1,并返回自增前的值

  • int getAndDecrement():

以原子方式将当前值自减1,并返回自减前的值

  • void lazySet(int newValue):

最终会设置成 newValue,注意这个方法是一个lazy方法,也就是说设置之后,并不会马上去将值更新到主内存,那么其他线程在一小段时间内可能看不到设置的值。

  • int getAndSet(int newValue):

以原子方式将值设置为newValue,并返回旧值

代码示例

package com.zwx.concurrent.atomic;

import java.util.concurrent.atomic.AtomicInteger;

public class TestAtomicBasicData {

public static void main(String[] args) throws InterruptedException {

AtomicInteger atomicInteger = new AtomicInteger(8);

System.out.println(“初始化:” + atomicInteger);//8

atomicInteger.compareAndSet(8,10);

System.out.println(“CAS后:” + atomicInteger);//10

System.out.println(atomicInteger.getAndIncrement());//自增1,返回自增前的值10

System.out.println(“自增后:” + atomicInteger);//11

System.out.println(atomicInteger.getAndDecrement());//11

System.out.println(“自减后:” + atomicInteger);//10

}

}

AtomicBoolean


更新boolean值,常用方法如下:

  • boolean compareAndSet(boolean expect,boolean update):

如果输入的数值等于预期值,则以原子方式将该值设置为输入的值,成功返回true,替换失败则返回false。注意这里面实际上会先将boolean值转换为int类型,0-否 1-是

在这里插入图片描述

  • void lazySet(boolan newValue):

最终会设置成 newValue,注意这个方法是一个lazy方法,也就是说设置之后,并不会马上去将值更新到主内存,那么其他线程在一小段时间内可能看不到设置的值。

  • boolean getAndSet(boolean newValue):

以原子方式将值设置为newValue,并返回旧值

在这里插入图片描述

AtomicLong


这个和上面的AtomicInteger几乎是一样的,就不在举例了。

原子操作都是利用Unsafe类中的CAS操作实现的,但是Unsafe中只提供了三种类型的CAS操作:

在这里插入图片描述

所以上面的boolean类型是转换为整型来CAS的,其他数据类型也可以先进行数据转换之后再通过CAS实现原子操作。

原子更新数组

===================================================================

原子操作更新数组也提供了三种类型:

  • AtomicIntegerArray:原子更Integer类型数组里的元素。

  • AtomicLongArray:原子更新Long类型数组里的元素。

  • AtomicReferenceArray:原子更新引用类型数组里的元素。

AtomicIntegerArray


int[] arr = new int[]{1,2,3};

AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(arr);

在这里插入图片描述

可以看到初始化之后将数组复制了一份,所以不会如果把值改变了,不会影响原有数组的值。

boolean compareAndSet(int i, int expect, int update):可以看到这个方法对比基本类型多了一个i,也就是index下标,其他方法也是一样和基本类型数组相比,多了一个index参数。

代码示例

package com.zwx.concurrent.atomic;

import java.util.concurrent.atomic.AtomicIntegerArray;

public class TestAtomicArray {

public static void main(String[] args) {

int[] arr = new int[]{1,2,3};

AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(arr);

atomicIntegerArray.compareAndSet(1,2,8);

System.out.println(arr[1]);//原数组的值没被改变,还是2

System.out.println(atomicIntegerArray.get(1));//atomicIntegerArray值被改变成8

}

}

AtomicLongArray


和基本类型AtomicLong相比,方法都一样,也是多了一个index数组下标参数。

AtomicReferenceArray


这个方法也是一样,唯一的区别是可以传入一个泛型,也就是说数据中的元素时自定义的对象,而不是引用对象。

代码示例:

package com.zwx.concurrent.atomic;

import com.alibaba.fastjson.JSONObject;

import java.util.concurrent.atomic.AtomicReferenceArray;

public class TestAtomicReferenceArray {

public static void main(String[] args) {

Man man = new Man(18,“张三”);

Man[] arr = new Man[]{man};

AtomicReferenceArray atomicReferenceArray = new AtomicReferenceArray<>(arr);

System.out.println(“CAS前:” + JSONObject.toJSONString(atomicReferenceArray.get(0)));//{“age”:18,“name”:“张三”}

Man updateMan = new Man(28,“李四”);

atomicReferenceArray.compareAndSet(0,man,updateMan);

System.out.println(“CAS前:” + JSONObject.toJSONString(atomicReferenceArray.get(0)));//{“age”:28,“name”:“李四”}

}

}

class Man{

protected volatile Integer age;

private String name;

public Man(Integer age, String name) {

this.age = age;

this.name = name;

}

public Integer getAge() {

return age;

}

public void setAge(Integer age) {

this.age = age;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

}

原子更新引用类型

=====================================================================

原子更新基本类型时每次只能更新一个变量,如果我们需要原子更新多个变量,怎么做呢?这时候我们可以把多个变量和合成一个,那么就需要使用这个原子更新引用类型提供的类来更新了。

原子更新引用类型也提供了3个类:

  • AtomicReference:原子更新引用类型。

  • AtomicMarkableReference:原子更新带有标记位的引用类型。可以原子更新一个布尔类 型的标记位和引用类型。

  • AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于原子的更新数据和数据的版本号,可以解决使用CAS进行原子更新时可能出现的ABA问题。

AtomicReference


需要先构造一个引用对象然后调用AtomicReference中的相关原子方法,我们先来看一段代码示例:

代码示例

package com.zwx.concurrent.atomic;

import com.alibaba.fastjson.JSONObject;

import java.util.concurrent.atomic.AtomicReference;

public class TestAtomicReference {

public static void main(String[] args) {

User oldUser = new User(18,“张三”);

AtomicReference atomicReference = new AtomicReference<>(oldUser);

System.out.println(“CAS前:” + JSONObject.toJSONString(atomicReference.get()));//{“age”:18,“name”:“张三”}

User upateUser = new User(28,“李四”);

boolean result =atomicReference.compareAndSet(oldUser,upateUser);

System.out.println(“CAS结果为:” + result);//true

System.out.println(“CAS后:” + JSONObject.toJSONString(atomicReference.get()));//{“age”:28,“name”:“李四”}

}

}

class User{

volatile Integer age;

private String name;

public User(Integer age, String name) {

this.age = age;

this.name = name;

}

public Integer getAge() {

return age;

}

public void setAge(Integer age) {

this.age = age;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

}

这里的原理也是一样,通过Uasafe的CAS操作Object对象实现原子操作:

在这里插入图片描述

AtomicMarkableReference


这个和上面AtomicReference基本一致,唯一的区别是多了一个mark标记,boolean类型。

示例

package com.zwx.concurrent.atomic;

import com.alibaba.fastjson.JSONObject;

import java.util.concurrent.atomic.AtomicMarkableReference;

public class TestAtomicReferenceMark {

public static void main(String[] args) {

Person person = new Person(18,“张三”);

AtomicMarkableReference atomicMarkableReference = new AtomicMarkableReference(person,false);

System.out.println(“是否被标记过:” + atomicMarkableReference.isMarked());

System.out.println(“CAS前:” + JSONObject.toJSONString(atomicMarkableReference.getReference()));//{“age”:18,“name”:“张三”}

Person updatePerson = new Person(28,“李四”);

/**

  • arg1:表示预期的引用对象

  • arg2:表示即将更新的引用对象

  • arg3:表示预期的标记

  • arg4:表示更新的标记

  • 需要参数1和参数3都是预期值才会CAS成功

*/

atomicMarkableReference.compareAndSet(person,updatePerson,false,true);

System.out.println(“CAS后:” + JSONObject.toJSONString(atomicMarkableReference.getReference()));//{“age”:28,“name”:“李四”}

}

}

class Person{

private Integer age;

private String name;

Docker步步实践

目录文档:

①Docker简介

②基本概念

③安装Docker

④使用镜像:

⑤操作容器:

⑥访问仓库:

⑦数据管理:

⑧使用网络:

⑨高级网络配置:

⑩安全:

⑪底层实现:

⑫其他项目:

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

⑤操作容器:

[外链图片转存中…(img-cJZnYyAj-1715049488242)]

⑥访问仓库:

[外链图片转存中…(img-zpsoGzJ7-1715049488242)]

⑦数据管理:

[外链图片转存中…(img-7bsnYOi5-1715049488243)]

⑧使用网络:

[外链图片转存中…(img-7yo0l2ov-1715049488243)]

⑨高级网络配置:

[外链图片转存中…(img-jN6n6CbF-1715049488243)]

⑩安全:

[外链图片转存中…(img-iGu6iXM4-1715049488244)]

⑪底层实现:

[外链图片转存中…(img-F9RCBx4s-1715049488244)]

⑫其他项目:

[外链图片转存中…(img-Be04c8GP-1715049488244)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 15
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值