侧边栏壁纸
博主头像
孔子说JAVA博主等级

成功只是一只沦落在鸡窝里的鹰,成功永远属于自信且有毅力的人!

  • 累计撰写 285 篇文章
  • 累计创建 125 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

Java并发编程之原子操作类AtomicReferenceFieldUpdater实战详解

孔子说JAVA
2022-06-13 / 0 评论 / 0 点赞 / 75 阅读 / 14,158 字 / 正在检测是否收录...

并发编程原子类完整教程:Java并发编程之原子操作类实战教程

AtomicReferenceFieldUpdater 是一个基于反射的工具类(原子操作),它能对指定类的 指定的volatile引用字段 进行 原子更新,需要注意的是这个字段不能是private的。简单理解就是对某个类中,被volatile修饰的字段进行原子更新。

  • AtomicReferenceFieldUpdater是 Doug Lea 在Java 5中写的atomic classes 中Filed Updater的一部分,本质上是volatile字段的包装器。相似的还有AtomicIntegerFieldUpdater。
  • ⼀个类中的volatile成员属性获取值、设定为某个值的这两个操作是⾮原⼦的,若想将其变为原⼦操作,可以通过 AtomicReferenceFieldUpdater 来实现。

1、AtomicReferenceFieldUpdater 的使用条件

1.1 应用场景

如果一个类是自己编写的,可以在编写的时候把成员变量定义为Atomic类型。但如果是一个已经有的类,在不能更改其源代码的情况下,要想实现对其成员变量的原子操作,就需要AtomicIntegerFieldUpdater、AtomicLongFieldUpdater 和 AtomicReferenceFieldUpdater。

1.2 使用条件

原子更新器 AtomicReferenceFieldUpdater 的使用存在比较苛刻的条件:

  • 操作的字段不能是static类型。

  • 操作的字段不能是final类型的,因为final根本没法修改。

  • 字段必须是volatile修饰的,也就是数据本身是读一致的。

  • 属性必须对当前的Updater所在的区域是可见的,如果不是当前类内部进行原子更新器操作不能使用private,protected子类操作父类时修饰符必须是protect权限及以上,如果在同一个package下则必须是default权限及以上,也就是说无论何时都应该保证操作类与被操作类间的可见性。

2、AtomicReferenceFieldUpdater 主要方法

2.1 实例的创建

AtomicReferenceFieldUpdater 有一个 protected 的无参数构造方法,只能供子类使用。所以一般情况下创建一个 AtomicReferenceFieldUpdater 实例需要使用该类提供的一个静态方法 newUpdater。

AtomicReferenceFieldUpdater 可以为一个用于更新指定类的声明为volatile类型的属性进行原子性更新,通过调用 AtomicReferenceFieldUpdater 的静态方法 newUpdater 创建实例,接受三个参数:

  • 第一个参数:包含要更新属性/字段的类的类型,即需要更新字段所在的class类
  • 第二个参数:更新属性/字段所属的类型,
  • 第三个参数:更新属性/字段的名称
AtomicReferenceFieldUpdater updater= AtomicReferenceFieldUpdater.newUpdater(Person .class, String.class, "name");  
Person person = new Person;
updater.compareAndSet(person ,person .name,"张三") ;  
System.out.println(Person .name); 
			
class  Person {
	volatile String name = "老刘";  
}

该示例代码通过调用 AtomicReferenceFieldUpdater.newUpdater(Person .class, String.class, "name") 静态方法生成 Person 类的 String 类型的 name 字段的原子修改器 updater,然后调用它的 compareAndSet 方法判断 person 对象的 name 值是否为 张三,若是则返回 true 并修改其值。也可调用 getAndSet 方法直接修改 person 的name字段值,并返回该字段原来的值。

2.2 主要方法

// 将此更新程序管理的给定对象的字段设置为给定的更新值。
set(T obj, V newValue)

// 获取此更新程序管理的给定对象的字段中保存的当前值。
get(T obj)

// 以原子方式将此更新程序管理的给定对象的字段设置为给定值并返回旧值。
getAndSet(T obj, V newValue)

// 最终将此更新程序管理的给定对象的字段设置为给定的更新值。
lazySet(T obj, V newValue)

// 如果当前值==预期值,则自动将此更新程序管理的给定对象的字段设置为给定的更新值。
compareAndSet(T obj, V expect, V update)

// 如果当前值==预期值,则自动将此更新程序管理的给定对象的字段设置为给定的更新值。
weakCompareAndSet(T obj, V expect, V update)

3、AtomicReferenceFieldUpdater 演示示例

3.1 演示示例1

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.function.BinaryOperator;
import java.util.function.LongBinaryOperator;
import java.util.function.UnaryOperator;
 
import org.junit.Test;
 
/**
 * AtomicReferenceFieldUpdater的测试类
 */
public class AtomicReferenceFieldUpdaterTest {
    //abstract
    private volatile Integer age;
 
    /**
     * 初始化采用静态方法+内部类,私有化构造函数
     * @throws
     */
    @Test
    public void testNewUpdater() {
        AtomicReferenceFieldUpdaterTest test=new AtomicReferenceFieldUpdaterTest();
        AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class,
                Integer.class,"age");
        System.out.println(updater.get(test));
    }
 
    /**
     * 如果指定对象的目标属性值为expect的值,则更新成新值,返回true
     * 否则不更新,返回false
     *
     * @throws
     */
    @Test
    public void testCompareAndSet() {
        AtomicReferenceFieldUpdaterTest test = new AtomicReferenceFieldUpdaterTest();
        AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class,
                Integer.class,"age");
        System.out.println(updater.compareAndSet(test, null, 2));
        System.out.println(updater.get(test));
    }
 
    /**
     * 如果指定对象的目标属性值为expect的值,则更新成新值,返回true
     * 否则不更新,返回false
     * weakCompareAndSet和compareAndSet暂时未发现区别
     *
     * @throws
     */
    @Test
    public void testWeakCompareAndSet() {
        AtomicReferenceFieldUpdaterTest test = new AtomicReferenceFieldUpdaterTest();
        AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class,
                Integer.class,"age");
        System.out.println(updater.weakCompareAndSet(test, null, 2));
        System.out.println(updater.get(test));
 
    }
 
    /**
     * 设置指定对象的目标属性值为新的值,unsafe.putIntVolatile设置值
     *
     * @throws
     */
    @Test
    public void testSet() {
        AtomicReferenceFieldUpdaterTest test = new AtomicReferenceFieldUpdaterTest();
        AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class,
                Integer.class,"age");
        updater.set(test, 22);
        System.out.println(updater.get(test));
    }
 
    /**
     * 设置指定对象的目标属性值为新的值,unsafe.putOrderedInt
     *
     * @throws
     */
    @Test
    public void testLazySet() {
        AtomicReferenceFieldUpdaterTest test = new AtomicReferenceFieldUpdaterTest();
        AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class,
                Integer.class,"age");
        updater.lazySet(test, 22);
        System.out.println(updater.get(test));
    }
 
    /**
     * 获取指定对象的目标属性值,需要检查是否越界
     *
     * @throws
     */
    @Test
    public void testGet() {
        AtomicReferenceFieldUpdaterTest test = new AtomicReferenceFieldUpdaterTest();
        AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class,
                Integer.class,"age");
        System.out.println(updater.get(test));
    }
 
    /**
     * 返回指定对象的目标属性值,并设置成新的值
     *
     * @throws
     */
    @Test
    public void testGetAndSet() {
        AtomicReferenceFieldUpdaterTest test = new AtomicReferenceFieldUpdaterTest();
        AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class,
                Integer.class,"age");
        System.out.println(updater.getAndSet(test, 32));
        System.out.println(updater.get(test));
    }
 
    /**
     * 返回指定对象的目标属性值,并更新成新值
     *
     * @throws
     */
    @Test
    public void testGetAndUpdate() {
        AtomicReferenceFieldUpdaterTest test = new AtomicReferenceFieldUpdaterTest();
        AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class,
                Integer.class,"age");
         UnaryOperator operator = new UnaryOperator() {
             @Override
             public Object apply(Object o) {
                 return new Integer(333);
             }
         };
        System.out.println(updater.getAndUpdate(test, operator));
        System.out.println(updater.get(test));
 
    }
 
    /**
     * 更新成新值并返回指定对象的目标属性值
     *
     * @throws
     */
    @Test
    public void testUpdateAndGet() {
        AtomicReferenceFieldUpdaterTest test = new AtomicReferenceFieldUpdaterTest();
        AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class,
                Integer.class,"age");
        UnaryOperator operator = new UnaryOperator() {
            @Override
            public Object apply(Object o) {
                return new Integer(333);
            }
        };
        System.out.println(updater.updateAndGet(test, operator));
        System.out.println(updater.get(test));
    }
    
    /**
     * 返回指定对象的目标属性值,并更新成新值
     *
     * @throws
     */
    @Test
    public void testGetAndAccumulate() {
        AtomicReferenceFieldUpdaterTest test = new AtomicReferenceFieldUpdaterTest();
        AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class,
                Integer.class,"age");
        BinaryOperator operator = new BinaryOperator() {
            @Override
            public Object apply(Object o, Object o2) {
                return o2;
            }
        };
        System.out.println(updater.getAndAccumulate(test, 2, operator));
        System.out.println(updater.get(test));
    }
 
    /**
     * 更新成新值并返回指定对象的目标属性值
     *
     * @throws
     */
    @Test
    public void testAccumulateAndGet() {
        AtomicReferenceFieldUpdaterTest test = new AtomicReferenceFieldUpdaterTest();
        AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class,
                Integer.class,"age");
        BinaryOperator operator = new BinaryOperator() {
            @Override
            public Object apply(Object o, Object o2) {
                return o2;
            }
        };
        System.out.println(updater.accumulateAndGet(test, 2, operator));
        System.out.println(updater.get(test));
    }
}

3.2 演示示例2

  1. 提供一个线程类,用来模拟多线程环境:
package com.securitit.serialize.atomics;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

public class AtomicReferenceThread extends Thread {
	// AtomicReference实例.
	private AtomicReference<String> atomicReference;
	// AtomicReferenceArray实例.
	private AtomicReferenceArray<String> atomicReferenceArray;
	// AtomicReferenceFieldUpdater实例.
	private AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterBean, String> atomicReferenceFieldUpdater;
	// AtomicReferenceFieldUpdaterBean实例.
	private AtomicReferenceFieldUpdaterBean atomicReferenceFieldUpdaterBean;
	// CountDownLatch可以进行线程计数,到达指定计数时,可唤醒调用await方法的线程.
	private CountDownLatch countDownLatch;

	public AtomicReferenceThread(AtomicReference<String> atomicReference,
			AtomicReferenceArray<String> atomicReferenceArray,
			AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterBean, String> atomicReferenceFieldUpdater,
			AtomicReferenceFieldUpdaterBean atomicReferenceFieldUpdaterBean, CountDownLatch countDownLatch) {
		this.atomicReference = atomicReference;
		this.atomicReferenceArray = atomicReferenceArray;
		this.atomicReferenceFieldUpdater = atomicReferenceFieldUpdater;
		this.atomicReferenceFieldUpdaterBean = atomicReferenceFieldUpdaterBean;
		this.countDownLatch = countDownLatch;
	}

	@Override
	public void run() {
		// 将atomicReference值重置为888.
		atomicReference.getAndSet("888");
		// 将AtomicReferenceArray值均重置为999.
		for (int index = 0; index < atomicReferenceArray.length(); index++) {
			atomicReferenceArray.getAndSet(index, "999");
		}
		// 将AtomicReferenceFieldUpdater值重置为000.
		if (atomicReferenceFieldUpdater.compareAndSet(atomicReferenceFieldUpdaterBean, "1", "000")) {
			System.out.println("AtomicReferenceFieldUpdater 更新成功.");
		} else {
			System.out.println("AtomicReferenceFieldUpdater 更新失败.");
		}
		countDownLatch.countDown();
	}

}
  1. 为方便 AtomicReferenceFieldUpdater 测试新建一个Bean类,类中只包含一个counter属性:
public class AtomicReferenceFieldUpdaterBean {

	// 内部计数器.
	public volatile String counter = "1";

	public String getCounter() {
		return counter;
	}

	public void setCounter(String counter) {
		this.counter = counter;
	}

}
  1. 编写测试类,开启多线程操作 AtomicReference、AtomicReferenceArray、AtomicReferenceFieldUpdater,以模拟多线程环境:
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

public class AtomicReferenceTester {
	// AtomicReference实例.
	private static AtomicReference<String> atomicReference;
	// AtomicReferenceArray实例.
	private static AtomicReferenceArray<String> atomicReferenceArray;
	// AtomicReferenceFieldUpdater实例.
	private static AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterBean, String> atomicReferenceFieldUpdater;
	// AtomicReferenceFieldUpdaterBean实例.
	private static AtomicReferenceFieldUpdaterBean atomicReferenceFieldUpdaterBean;
	// CountDownLatch可以进行线程计数,到达指定计数时,可唤醒调用await方法的线程.
	private static CountDownLatch countDownLatch = new CountDownLatch(4);

	public static void main(String[] args) throws Exception {
		// 初始化AtomicReference实例.
		atomicReference = new AtomicReference<String>("1");
		// 初始化AtomicReferenceArray实例.
		atomicReferenceArray = new AtomicReferenceArray<String>(10);
		// 初始化AtomicReferenceArray实例数组内部值.
		for (int index = 0; index < atomicReferenceArray.length(); index++) {
			// 用当前索引作为值.
			atomicReferenceArray.set(index, index + "");
		}
		// 初始化AtomicReferenceFieldUpdater实例.
		// 第一个参数引用类型类型Class,第二个参数为成员变量类型Class,第三个参数为成员变量名称.
		atomicReferenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterBean.class,
				String.class, "counter");
		// 初始化AtomicReferenceFieldUpdaterBean实例.
		atomicReferenceFieldUpdaterBean = new AtomicReferenceFieldUpdaterBean();
		atomicReferenceFieldUpdaterBean.setCounter("1");
		// 启动多线程测试.
		new AtomicReferenceThread(atomicReference, atomicReferenceArray, atomicReferenceFieldUpdater,
				atomicReferenceFieldUpdaterBean, countDownLatch).start();
		new AtomicReferenceThread(atomicReference, atomicReferenceArray, atomicReferenceFieldUpdater,
				atomicReferenceFieldUpdaterBean, countDownLatch).start();
		new AtomicReferenceThread(atomicReference, atomicReferenceArray, atomicReferenceFieldUpdater,
				atomicReferenceFieldUpdaterBean, countDownLatch).start();
		new AtomicReferenceThread(atomicReference, atomicReferenceArray, atomicReferenceFieldUpdater,
				atomicReferenceFieldUpdaterBean, countDownLatch).start();
		// 等待所有线程执行完毕.
		countDownLatch.await();
		// 输出AtomicReference的值.
		System.out.println("AtomicReference:" + atomicReference.get());
		// 输出AtomicReferenceArray数值.
		for (int index = 0; index < atomicReferenceArray.length(); index++) {
			if(index != 0) {
				continue;
			}
			System.out.println("AtomicReferenceArray:" + atomicReferenceArray.get(index));
		}
		// 输出atomicReferenceFieldUpdaterBean的值.
		System.out.println("AtomicReferenceFieldUpdater:" + atomicReferenceFieldUpdaterBean.getCounter());
	}

}

程序的运行结果:

AtomicReferenceFieldUpdater 更新失败.
AtomicReferenceFieldUpdater 更新失败.
AtomicReferenceFieldUpdater 更新失败.
AtomicReferenceFieldUpdater 更新成功.
AtomicReference:888
AtomicReferenceArray:999
AtomicReferenceFieldUpdater:000

从结果可以看出:

  • AtomicReference可以成功更新类型变量的值。
  • AtomicReferenceArray可以成功更新类型变量的值。
  • AtonicReferenceFieldUpdater可以成功更新指定Bean的某属性。

4、源码分析

AtomicReferenceFieldUpdater 的代码很简单,下面对 AtomicReferenceFieldUpdater 部分源码进行解析。

4.1 实现基础 - 构造方法

AtomicReferenceFieldUpdater 有一个 protected 的构造方法,只能供子类使用。所以一般情况下创建一个 AtomicReferenceFieldUpdater 实例需要使用该类提供的一个静态方法 newUpdater。

// 受保护的无操作构造方法,供子类使用。
protected AtomicReferenceFieldUpdater()

4.2 实现基础 - 创建实例的静态方法

创建实例的静态方法为 newUpdater:

public static <U,W> AtomicReferenceFieldUpdater<U,W> newUpdater(Class<U> tclass,
                                                                Class<W> vclass,
                                                                String fieldName)

参数:

  • tclass - 保持字段的对象类。
  • vclass - 该字段的类
  • fieldName - 要更新的字段名称。

返回:

  • 更新程序

抛出:

  • IllegalArgumentException - 如果该字段不是可变引用类型。
  • RuntimeException - 如果该类不保持字段,或者是错误的类型,将抛出 RuntimeException 和一个嵌套的基于反射的异常。

4.3 实现基础 - 主要方法

4.3.1 set方法

set 方法设置由此更新程序所管理的给定对象的字段。对于 compareAndSet 的后续调用,此操作可以确保充当可变存储。

public abstract void set(T obj,
                         V newValue)

参数:

  • obj - 要设置其字段的对象
  • newValue - 新值

4.3.2 get方法

get 方法获取由给定对象在字段中保持的当前值。

public abstract V get(T obj)

参数:

  • obj - 要获取其字段的对象

返回:

  • 当前值

4.3.3 lazySet方法

lazySet 方法最终将此更新程序管理的给定对象的字段设置为给定的更新值。

// 最终将此更新程序管理的给定对象的字段设置为给定的更新值。
lazySet(T obj, V newValue)

lazySet与set实现的功能类似,二者区别如下:

  • lazySet实现的是最终一致性,set实现的是强一致性
  • lazySet:多线程并发时,线程A调用unsafe.putOrderedInt更新newValue值时,只是把线程A内存中的元素更新成功了,但其它线程此时看不到线程A更新的newValue值,需过一会,线程A更新的newValue才会刷入到主内存中,此时其它线程才能看到线程A更新的值
  • set: 多线程并发时,线程A更新的值会立即刷入主内存中
  • 总结:对于数据一致性要求高的建议用set

4.3.4 获取设置方法

getAndSet 方法为获取设置方法,即以原子方式将此更新程序管理的给定对象的字段设置为给定值并返回旧值。

public V getAndSet(T obj,
                   V newValue)

参数:

  • obj - 要获取并设置其字段的对象
  • newValue - 新值

返回:

  • 以前的值

4.3.5 比较设置方法compareAndSet

比较设置方法有 compareAndSet 和 weakCompareAndSet。

compareAndSet()方法: 如果当前值 == 预期值,则以原子方式将此更新程序所管理的给定对象的字段值设置为给定的更新值。对 compareAndSet 和 set 的其他调用,此方法可以确保原子性,但对于字段中的其他更改则不一定确保原子性。

// 如果当前值==预期值,则自动将此更新程序管理的给定对象的字段设置为给定的更新值。
compareAndSet(T obj, V expect, V update)

参数:

  • obj - 有条件地设置其字段的对象
  • expect - 预期值
  • update - 新值

返回:

  • 如果成功,则返回 true。

4.3.6 比较设置方法weakCompareAndSet

比较设置方法有 compareAndSet 和 weakCompareAndSet。

weakCompareAndSet()方法: 如果当前值 == 预期值,则以原子方式将此更新程序所管理的给定对象的字段值设置为给定的更新值。对 compareAndSet 和 set 的其他调用,此方法可以确保原子性,但对于字段中的其他更改则不一定确保原子性,并且可能会意外失败。

// 如果当前值==预期值,则自动将此更新程序管理的给定对象的字段设置为给定的更新值。
weakCompareAndSet(T obj, V expect, V update)

参数:

  • obj - 有条件地设置其字段的对象
  • expect - 预期值
  • update - 新值

返回:

  • 如果成功,则返回 true。
0

评论区