AtomicReference赋值操作是非线程安全的,一般会在数据库中用锁来实现变更赋值,如果不用锁,可以用AtomicReference<V>类,实现将对象进行原子操作。提供了一种读和写都是原子性的对象引用变量。原子意味着多个线程试图改变同一个AtomicReference(例如比较和交换操作)将不会使得AtomicReference处于不一致的状态。
一、源码解析
public class AtomicReference<V> implements Serializable {
private static final long serialVersionUID = -1848883965231344442L;
private static final Unsafe unsafe = Unsafe.getUnsafe();//通过unsafe实现原子操作
private static final long valueOffset;
private volatile V value;//通过volatile实现原子操作
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicReference.class.getDeclaredField("value"));
} catch (Exception ex) {
throw new Error(ex);
}
}
public AtomicReference(V var1) {
this.value = var1;
}
public AtomicReference() {
}
//通过原子操作获取当前值
public final V get() {
return this.value;
}
//不是原子操作-慎用
public final void set(V var1) {
this.value = var1;
}
//通过 unsafe 实现原子操作
public final void lazySet(V var1) {
unsafe.putOrderedObject(this, valueOffset, var1);
}
//通过 unsafe 实现原子操作
/**
* var1当前值:拿当前值value和var1值去比较,如果相等返回true并更新值为var2期望值
* var2期望值:如果返回true则更新为期望值,如果返回false则不更新值
*/
public final boolean compareAndSet(V var1, V var2) {
return unsafe.compareAndSwapObject(this, valueOffset, var1, var2);
}
//通过 unsafe 实现原子操作
public final boolean weakCompareAndSet(V var1, V var2) {
return unsafe.compareAndSwapObject(this, valueOffset, var1, var2);
}
//原子赋值新值并且返回旧值
public final V getAndSet(V var1) {
return unsafe.getAndSetObject(this, valueOffset, var1);
}
}
二、应用
public class TestClass {
public static void main(String[] args){
Customer Customer1 =new Customer("Lucy", BigDecimal.valueOf(99l));
Customer Customer2 =new Customer("Lily",BigDecimal.valueOf(99l));
final AtomicReference<Customer> reference = new AtomicReference<>(Customer1);
Customer customer3 = reference.get();
if (customer3.equals(Customer1)) {
System.out.println("Customer3:" + customer3+"name:"+customer3.getName()+"*****"+"value"+customer3.getAmount());
} else {
System.out.println("else:" + customer3+"name:"+customer3.getName()+"*****"+"value"+customer3.getAmount());
}
boolean b = reference.compareAndSet(null, Customer2);
System.out.println("myClass.main-"+b+"--"+reference.get()+"name:"+reference.get().getName()+"*****"+"value"+reference.get().getAmount());
boolean b1 = reference.compareAndSet(Customer1, Customer2);
System.out.println("myClass.main-"+b1+"--"+reference.get()+"name:"+reference.get().getName()+"*****"+"value"+reference.get().getAmount());
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread1-----------");
Customer Customer = reference.get();
Customer.setName("Lucy1");
Customer.setAmount(Customer.getAmount().add(BigDecimal.valueOf(100l)));
reference.getAndSet(Customer);
System.out.println("Thread1:"+reference.get().toString()+"name:"+reference.get().getName()+"*****"+"value"+reference.get().getAmount());
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread2-----------");
Customer Customer = reference.get();
Customer.setName("Lucy2");
Customer.setAmount(Customer.getAmount().add(BigDecimal.valueOf(100l)));
reference.getAndSet(Customer);
System.out.println("Thread2:"+reference.get().toString()+"name:"+reference.get().getName()+"*****"+"value"+reference.get().getAmount());
}
}).start();
}
}
执行结果
Customer3:com.project.domain.Customer@13f6c937name:Lucy*****value99
myClass.main-false--
com.project.domain.Customer@13f6c937name:Lucy*****value99
myClass.main-true--
com.project.domain.Customer@69b3d448name:Lily*****value99
Thread1-----------
Thread1:com.project.domain.Customer@69b3d448name:Lucy1*****value199
Thread2-----------
Thread2:com.project.domain.Customer@69b3d448name:Lucy2*****value299
根据结果看线程是安全的,thread1线程执行结束,才开始执行thread2.
三、应用拓展
除了AtomicReference可以保证多线程操作安全,Java库还有AtomicInteger、AtomicBoolean、AtomicLong保证多线程操作的安全性。