AtomicReference

The AtomicReference class provides an object reference variable that can be read and written atomically. Atomic means that multiple threads attempting to change the same AtomicReference (for example, using comparison and swap operations) do not end up with an inconsistent AtomicReference. AtomicReference even has an advanced compareAndSet () method that compares the reference to the expected value (the reference) and, if they are equal, sets a new reference within the AtomicReference object.

AtomicStampReference securely changes the value of a variable

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 class description * @author: MAC * @date: 2020/12/29 **/ // Thread safety public class AtomicIntegerTest {private static AtomicReference<Integer> count=new AtomicReference<>(0); Public static void main(String[] args) {count.compareAndSet(0, 2); Count.com pareAndSet(1, 4); // pareAndSet(1, 4); Count.com pareAndSet(2, 8); count.compareAndSet(2, 8); //ok System.out.println(count.get()); }} // Output 8Copy the code

ifAtomicReference<T>In theTIs it a custom object, thread-safe?

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() {} /** * does not need security */ public final V get() { return value; } public final void set(V newValue) {value = newValue;} public final void set(V newValue) {value = newValue; } /** * the csa operation is obviously called. * Compare objects and set values. * Return true if the values are set. Otherwise it returns false * / public final Boolean compareAndSet (update) V expect, V {return unsafe.com pareAndSwapObject (this, valueOffset, expect, update); } /** * set the newValue and return the old value * atomic operation */ @suppresswarnings ("unchecked") public final V getAndSet(V newValue) {return (V)unsafe.getAndSetObject(this, valueOffset, newValue); }}Copy the code

CompareAndSet uses CAS to ensure concurrency

Some of the methods provided by AtomicReference allow atomicity, such as compareAndSet, getAndSet, which is atomicity only for references

AtomicReference cannot guarantee that the modification of attribute values in the object is thread-safe. For example, if the reference object is a person and the name and age in the person are modified, multiple threads obtain the object from the reference at the same time and modify it, the thread-unsafe situation will occur. Let’s test this conclusion with some code.

AtomicReferenceUnsafely modifies the value of a custom object property

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; /** * @name: AtomicReferenceTest * @description: TODO class description * @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()); } // The age value is the same as the name value if the thread is thread safe // The age value is different from the name value if the thread is thread safe. 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; }}Copy the code

The correct results can be obtained at low concurrency, but the difference can occur at high concurrency. Because custom objects are accessed using set and GET does not have CAS, the thread is not safe.

throughAtomicintegerFieldUpdaterSafely modify custom objects

Atomic package provides AtomicReferenceFieldUpdater, AtomicIntegerFieldUpdater, AtomicLongFieldUpdater, atomic updates a class instance specified a certain field.

AtomicIntegerFieldUpdater

Change the variable value using the CAS

package com.keytech.task; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; / * * * @ the className: AtomicIntegerFieldUpdaterTest * @ description: TODO class description * @ author: MAC * @ the 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 (" modified successfully "+obj.getCount())); } if(upCount.compareAndSet(obj,100,200)){system.out.println (" modified successfully "); }else{system.out.println (" failed to modify "); 200 // Failed to modifyCopy the code

AtomicIntegerFieldUpdaterSource code analysis

Public abstract class AtomicIntegerFieldUpdater < T > {/ * * * * @ param tclass holds classes in a field * @ param fieldName field name * / @CallerSensitive public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass, String fieldName) { return new AtomicIntegerFieldUpdaterImpl<U> (tclass, fieldName, Reflection.getCallerClass()); } /** * atomization */ 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); NoSuchFieldException {return tclass.getdeclAredField (fieldName); }}); // Check access level 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"); // Must use 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.objectfieldoffset (field) = unsafe.objectFieldoffset (field); // Unsafe.objectFieldoffset (field) = unsafe.objectFieldoffset (field); }}}Copy the code

AtomicIntegerFieldUpdater thread-safe update custom object property values

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; /** * @name: AtomicReferenceTest * @description: TODO class description * @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{// the wrapper type must be volatile if it is an Integer. 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=5000Copy the code