AtomicReference类提供了一个可以原子读写的对象引用变量。 原子意味着尝试更改相同AtomicReference的多个线程(例如,使用比较和交换操作)不会使AtomicReference最终达到不一致的状态。 AtomicReference甚至有一个先进的compareAndSet()方法,它可以将引用与预期值(引用)进行比较,如果它们相等,则在AtomicReference对象内设置一个新的引用。
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
包中提供AtomicReferenceFieldUpdater
、AtomicIntegerFieldUpdater
、AtomicLongFieldUpdater
,原子性的更新某一个类实例的指定的某一个字段.
通过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);
}
}
}
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
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。