关于多线程:AtomicReference原子性引用

44次阅读

共计 8022 个字符,预计需要花费 21 分钟才能阅读完成。

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

正文完
 0