栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【并发编程系列11】Java中12个原子(Atomic)操作类实现原理分析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【并发编程系列11】Java中12个原子(Atomic)操作类实现原理分析

  • 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,“李四”);

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;

public Person(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;

}

}

AtomicMarkableReference原理分析

初始化对象的时候需要初始化一个引用对象和一个初始mark,而这两个属性又是通过其静态内部类Pair来管理的:

AtomicStampedReference


这个和AtomicMarkableReference几乎一模一样,唯一的区别就是AtomicMarkableReference中的标记只有true和false,而AtomicStampedReference中的标记是一个int类型,可以视作版本号,可以解决CAS的ABA问题。

原子更新属性

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

  • AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。

  • AtomicLongFieldUpdater:原子更新长整型字段的更新器。

  • AtomicReferenceFieldUpdater:原子更新引用类型里的任意指定字段。

AtomicIntegerFieldUpdater


这个是用来更新引用对象中的int类型的属性,利用反射修改属性。有以下几点需要注意:

  • 引用类型中的属性必须是int,不能是包装类Integer

  • 引用类型中的属性必须被volatile修饰

  • 引用类型中的属性不能被private修饰

代码示例

package com.zwx.concurrent.atomic;

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

public class TestAtomicReferenceField {

public static void main(String[] args) {

//AtomicIntegerFieldUpdater

Women women = new Women(18,“张三”);

//arg1:引用的对象类型 arg2:要修改的对象中的属性名

AtomicIntegerFieldUpdater atomicIntegerFieldUpdater = AtomicIntegerFieldUpdater.newUpdater(Women.class,“age”);

atomicIntegerFieldUpdater.compareAndSet(women,18,28);

System.out.println(“CAS后的值:” + women.getAge());//28

}

}

class Women{

volatile int age;

private String name;

public Women(int 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;

}

}

newUpdater方法:

AtomicIntegerFieldUpdaterImpl中初始化中就是利用反射获取属性修改属性,并进行了一些验证:

AtomicLongFieldUpdater


这个和上面AtomicLongFieldUpdater基本一样,用来更新long类型的属性,同样有以下几点需要注意:

  • 引用类型中的属性必须是long,不能是包装类Long

  • 引用类型中的属性必须被volatile修饰

  • 引用类型中的属性不能被private修饰

AtomicReferenceFieldUpdater


上面两个都是只能更新指定数据类型,而这个可以更新任意指定类型的属性。也有以下几个注意点:

  • 引用类型中的属性不能是原始数据类型,必须用对应包装类(这点和上面的两种相反)

  • 引用类型中的属性必须被volatile修饰

  • 引用类型中的属性不能被private修饰

示例

package com.zwx.concurrent.atomic;

import com.alibaba.fastjson.JSONObject;

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

public class TestAtomicReferenceField {

public static void main(String[] args) {

AtomicReferenceFieldUpdater atomicReferenceFieldUpdater1 = AtomicReferenceFieldUpdater.newUpdater(Women.class,Integer.class,“age”);

AtomicReferenceFieldUpdater atomicReferenceFieldUpdater2 = AtomicReferenceFieldUpdater.newUpdater(Women.class,String.class,“name”);

Women women = new Women(18,“张三”);

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/678702.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号