专栏首页JVMGCAtomicReference原子性引用
原创

AtomicReference原子性引用

AtomicReference

AtomicReference类提供了一个可以原子读写的对象引用变量。 原子意味着尝试更改相同AtomicReference的多个线程(例如,使用比较和交换操作)不会使AtomicReference最终达到不一致的状态。 AtomicReference甚至有一个先进的compareAndSet()方法,它可以将引用与预期值(引用)进行比较,如果它们相等,则在AtomicReference对象内设置一个新的引用。

AtomicStampReference 安全的修改一个变量的值

package com.keytech.task;

import org.junit.platform.commons.logging.LoggerFactory;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @className: AtomicIntegerTest
 * @description: TODO 类描述
 * @author: mac
 * @date: 2020/12/29
 **/

//线程安全

public class AtomicIntegerTest {
    private static AtomicReference<Integer> count=new AtomicReference<>(0);
    public static void main(String[] args) {
        //如果期望值是0,则修改成2
        count.compareAndSet(0, 2); //ok
        //如果期望值是1,则修改成4
        count.compareAndSet(1, 4); //no ok
        //如果期望值是2,则修改成8
        count.compareAndSet(2, 8); //ok
        System.out.println(count.get());
    }
}

//输出8

如果AtomicReference<T>T是一个自定义的对象,线程安全?

public class AtomicReference<V> implements java.io.Serializable {
    private static final long serialVersionUID = -1848883965231344442L;

    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicReference.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile V value;

    /**
     * Creates a new AtomicReference with the given initial value.
     *
     * @param initialValue the initial value
     */
    public AtomicReference(V initialValue) {
        value = initialValue;
    }

    /**
     * Creates a new AtomicReference with null initial value.
     */
    public AtomicReference() {
    }

    /**
     * 不需要安全防护
     */
    public final V get() {
        return value;
    }

    /**
     * 设值值不需要进行对象安全防护
     */
    public final void set(V newValue) {
        value = newValue;
    }


    /**
     * 很明显调用的是csa操作
     * 比较对象是否相同,进行设值
     * 设值成功返回true,否则返回false
     */
    public final boolean compareAndSet(V expect, V update) {
        return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
    }

    /**
     * 设置新的值并且返回旧的值
     * 原子操作
     */
    @SuppressWarnings("unchecked")
    public final V getAndSet(V newValue) {
        return (V)unsafe.getAndSetObject(this, valueOffset, newValue);
    }
}

compareAndSet采用CAS保证并发

AtomicReference 所提供的某些方法可以进行原子性操作,如compareAndSet、getAndSet,这仅仅是对引用进行原子性操作

AtomicReference 不能保证对象中若存在属性值修改是线程安全的,如假设引用对象是person,修改person中name和age,多个线程同时从引用中获得对象,并进行修改,会出现线程不安全情况。下面我们通过代码来验证一下这条结论。

AtomicReference不安全的修改自定义对象属性的值

package com.keytech.task;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @className: AtomicReferenceTest
 * @description: TODO 类描述
 * @author: mac
 * @date: 2020/12/29
 **/
public class AtomicReferenceTest {

    private static Integer clientTotal=5000;
    private static Integer threadTotal=200;
    private static Rumenz rumenz=new Rumenz(0,0);

    private static AtomicReference<Rumenz> rumenzReference=new AtomicReference<>(rumenz);
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        Semaphore semaphore=new Semaphore(threadTotal);
        for (int i = 0; i < clientTotal; i++) {
            final  Integer n=i;
            executorService.execute(()->{
                 try{
                    semaphore.acquire();
                     update(n);
                    semaphore.release();

                 }catch (Exception e){
                     e.printStackTrace();
                 }
            });
        }

        executorService.shutdown();
        System.out.println("rumenzReference="+rumenzReference.get().getAge());
 System.out.println("rumenzReference="+rumenzReference.get().getName());
    }
    //如果线程安全的话,age的值和name的值是一致的
    //如果线程不安全的话,age的值和name是不一样的。
    private static void update(int i){
        rumenzReference.get().setAge(rumenzReference.get().getAge()+i);
        rumenzReference.get().setName(rumenzReference.get().getName()+i);
    }
}

class Rumenz{
    private Integer age;
    private Integer name;
    public Rumenz(Integer age, Integer name) {
        this.age = age;
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Integer getName() {
        return name;
    }

    public void setName(Integer name) {
        this.name = name;
    }
}

在低并发情况下可以得到正确的结果,但是高并发情况下就会出现差异.因为自定义的对象在访问时用的是set,get没有CAS,所以导致线程不安全.

通过AtomicintegerFieldUpdater安全的修改自定义对象

atomic包中提供AtomicReferenceFieldUpdaterAtomicIntegerFieldUpdaterAtomicLongFieldUpdater,原子性的更新某一个类实例的指定的某一个字段.

AtomicIntegerFieldUpdater

通过CAS修改变量值

package com.keytech.task;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

/**
 * @className: AtomicIntegerFieldUpdaterTest
 * @description: TODO 类描述
 * @author: mac
 * @date: 2020/12/29
 **/
public class AtomicIntegerFieldUpdaterTest {


    private static AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> upCount=AtomicIntegerFieldUpdater.newUpdater(AtomicIntegerFieldUpdaterTest.class,"count");

    public int getCount() {
        return count;
    }

    public volatile int count=100;

    public static void main(String[] args) {
        AtomicIntegerFieldUpdaterTest obj=new AtomicIntegerFieldUpdaterTest();
        if(upCount.compareAndSet(obj,100,200)){
            System.out.println("修改成功"+obj.getCount());
        }
        if(upCount.compareAndSet(obj,100,200)){
            System.out.println("修改成功");
        }else{
            System.out.println("修改失败");
        }


    }
}

//修改成功200
//修改失败

AtomicIntegerFieldUpdater源码分析

public abstract class AtomicIntegerFieldUpdater<T> {
    /**
     *
     * @param tclass 持有某字段的类
     * @param fieldName 字段名字
     */
    @CallerSensitive
    public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass,
                                                              String fieldName)     {
        return new AtomicIntegerFieldUpdaterImpl<U>
            (tclass, fieldName, Reflection.getCallerClass());
    }
    
    /**
     * 原子性设置
     */
    public int getAndSet(T obj, int newValue) {
        int prev;
        do {
            prev = get(obj);
        } while (!compareAndSet(obj, prev, newValue));
        return prev;
    }
    
    private static class AtomicIntegerFieldUpdaterImpl<T>
            extends AtomicIntegerFieldUpdater<T> {
        private static final Unsafe unsafe = Unsafe.getUnsafe();
        private final long offset;
        private final Class<T> tclass;
        private final Class<?> cclass;

        AtomicIntegerFieldUpdaterImpl(final Class<T> tclass,
                                      final String fieldName,
                                      final Class<?> caller) {
            final Field field;
            final int modifiers;
            try {
                field = AccessController.doPrivileged(
                    new PrivilegedExceptionAction<Field>() {
                        public Field run() throws NoSuchFieldException {
                            //字段不存在会抛异常
                            return tclass.getDeclaredField(fieldName);
                        }
                    });
                 //检查访问级别
                modifiers = field.getModifiers();
                sun.reflect.misc.ReflectUtil.ensureMemberAccess(
                    caller, tclass, null, modifiers);
                ClassLoader cl = tclass.getClassLoader();
                ClassLoader ccl = caller.getClassLoader();
                if ((ccl != null) && (ccl != cl) &&
                    ((cl == null) || !isAncestor(cl, ccl))) {
                  sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
                }
            } catch (PrivilegedActionException pae) {
                throw new RuntimeException(pae.getException());
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }

            Class<?> fieldt = field.getType();
            //必须是int
            if (fieldt != int.class)
                throw new IllegalArgumentException("Must be integer type");
            //必须用volatile修饰
            if (!Modifier.isVolatile(modifiers))
                throw new IllegalArgumentException("Must be volatile type");

            this.cclass = (Modifier.isProtected(modifiers) &&
                           caller != tclass) ? caller : null;
            this.tclass = tclass;
            //用Unsafe里的那一坨方法去原子更新
            offset = unsafe.objectFieldOffset(field);
        }
    }
}

AtomicIntegerFieldUpdater线程安全的更新自定义对象的属性值

package com.keytech.task;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
/**
 * @className: AtomicReferenceTest
 * @description: TODO 类描述
 * @author: mac
 * @date: 2020/12/29
 **/
public class AtomicReferenceTest {

    private static Integer clientTotal=5000;
    private static Integer threadTotal=200;

    public static Rumenz rumenz=new Rumenz(0,0);
    public static AtomicIntegerFieldUpdater<Rumenz> rumenzReferenceAge= AtomicIntegerFieldUpdater.newUpdater(Rumenz.class,"age");
   private static AtomicIntegerFieldUpdater<Rumenz> rumenzReferenceName= AtomicIntegerFieldUpdater.newUpdater(Rumenz.class,"name");

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        Semaphore semaphore=new Semaphore(threadTotal);
        for (int i = 0; i < clientTotal; i++) {
            final  Integer n=i;
            executorService.execute(()->{
                 try{
                    semaphore.acquire();
                     update(n);
                    semaphore.release();

                 }catch (Exception e){
                     e.printStackTrace();
                 }
            });
        }

        executorService.shutdown();
        System.out.println("rumenzReference="+ rumenz.getAge());
        System.out.println("rumenzReference="+ rumenz.getName());
    }

    public static void update(int i){
        rumenzReferenceAge.incrementAndGet(rumenz);
        rumenzReferenceName.incrementAndGet(rumenz);
    }
}

class Rumenz{

    //必须加volatile 如果是整数不能写包装类型:如不能为Integer
    public volatile  int age;
    public volatile  int name;

    public Rumenz(Integer age, Integer name) {
        this.age = age;
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Integer getName() {
        return name;
    }

    public void setName(Integer name) {
        this.name = name;
    }
}

//rumenzReference=5000
//rumenzReference=5000
wx.jpg

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 突击并发编程JUC系列-原子引用AtomicReference

    小伙伴们,大家好,我们又见面了,突击并发编程 JUC 系列实战原子引用马上就要发车了。

    山间木匠
  • Java并发编程-原子性变量

    AtomicBoolean 类为我们提供了一个可以用原子方式进行读和写的布尔值,它还拥有一些先进的原子性操作,比如 compareAndSet()。Atomic...

    高广超
  • 为什么要有 AtomicReference ?

    我们之前了解过了 AtomicInteger、AtomicLong、AtomicBoolean 等原子性工具类,下面我们继续了解一下位于 java.util.c...

    cxuan
  • JDK中Concurrent包工具类指南

    这篇翻译指南很能解决问题,对于初步建立并发包的认识很有帮助,感谢原作者和翻译者 Java 并发工具包 java.util.concurrent 用户指南 ...

    于霆霖
  • 并发编程- java.util.concurrent用户指南

    本指南根据 Jakob Jenkov 最新博客翻译,请随时关注博客更新:http://tutorials.jenkov.com/java-util-concur...

    高广超
  • 【进阶之路】并发编程(三)-非阻塞同步机制

    .markdown-body{word-break:break-word;line-height:1.75;font-weight:400;font-size:...

    南橘
  • 并发编程中的原子类

    对多线程访问同一个变量,我们需要加锁,而锁是比较消耗性能的,JDk1.5之后, 新增的原子操作类提供了 一种用法简单、性能高效、线程安全地更新一个变量的方式,...

    Dream城堡
  • Java原子操作类,你知道多少?

    由于synchronized是采用的是悲观锁策略,并不是特别高效的一种解决方案。 实际上,在J.U.C下的atomic包提供了一系列的操作简单,性能高效,并能保...

    李红
  • Java原子操作类,你知道多少?

    由于synchronized是采用的是悲观锁策略,并不是特别高效的一种解决方案。 实际上,在J.U.C下的atomic包提供了一系列的操作简单,性能高效,并能保...

    李红
  • 全面了解 Java 原子变量类

    保证线程安全是 Java 并发编程必须要解决的重要问题。Java 从原子性、可见性、有序性这三大特性入手,确保多线程的数据一致性。

    静默虚空
  • Java并发编程之原子变量

    原子变量比锁的粒度更细,量级更轻,并且对于在多处理器系统上实现高性能的并发代码来说是非常关键的。

    李红
  • Java多线程之CAS

    CAS字面意思为比较并交换.CAS 有 3 个操作数,分别是:内存值 M,期望值 E,更新值 U。当且仅当内存值 M 和期望值 E 相等时,将内存值 M 修改为...

    Java宝典
  • 非阻塞算法(Lock-Free)的实现

    上篇文章我们讲到了使用锁会带来的各种缺点,本文将会讲解如何使用非阻塞算法。非阻塞算法一般会使用CAS来协调线程的操作。

    程序那些事
  • java并发包(1)-AtomicReference和AtomicStampedReference

    AtomicReference原子应用类,可以保证你在修改对象引用时的线程安全性,比较时可以按照偏移量进行

    yiduwangkai
  • 尝试Java加锁新思路:原子变量和非阻塞同步算法

    前文中曾经对比同步方法的内置锁相比和显式锁,来说明它们各自的优势,但是无论是内置说还是显式锁,其本质都是通过加锁来维护多线程安全。

    lyb-geek
  • CAS Krains 2020-08-25

    其中的关键是 compareAndSet,它的简称就是 CAS (也有 Compare And Swap 的说法),该方法是原子的。

    Krains
  • "聊胜于无",浅析Java中的原子操作Unsafe类

    Java放弃了指针,获得了更高的安全性和内存自动清理的能力。但是,它还是在一个角落里提供了类似于指针的功能,那就是sun.misc.Unsafe类,利用这个类,...

    JavaEdge
  • 引用类型原子类

    基本类型原子类只能更新一个变量,如果需要原子更新多个变量,需要使用 引用类型原子类。

    黑洞代码
  • Java面试之CAS

    功能:判断内存某个位置的L值是否为预期值,如果是则更改为新的值,这个过程是原子的。

    乐心湖

扫码关注云+社区

领取腾讯云代金券