【实战Java高并发程序设计】3:带有时间戳的对象引用:AtomicStampedReference
2016-10-22 00:00
316 查看
AtomicReference无法解决上述问题的根本是因为对象在修改过程中,丢失了状态信息。对象值本身与状态被画上了等号。因此,我们只要能够记录对象在修改过程中的状态值,就可以很好的解决对象被反复修改导致线程无法正确判断对象状态的问题。
AtomicStampedReference正是这么做的。它内部不仅维护了对象值,还维护了一个时间戳(我这里把它称为时间戳,实际上它可以使任何一个整数,它使用整数来表示状态值)。当AtomicStampedReference对应的数值被修改时,除了更新数据本身外,还必须要更新时间戳。当AtomicStampedReference设置对象值时,对象值以及时间戳都必须满足期望值,写入才会成功。因此,即使对象值被反复读写,写回原值,只要时间戳发生变化,就能防止不恰当的写入。
AtomicStampedReference的几个API在AtomicReference的基础上新增了有关时间戳的信息:
有了AtomicStampedReference这个法宝,我们就再也不用担心对象被写坏啦!现在,就让我们使用AtomicStampedReference在修正那个贵宾卡充值的问题的:
第2行,我们使用AtomicStampedReference代替原来的AtomicReference。第6行获得账户的时间戳。后续的赠予操作以这个时间戳为依据。如果赠予成功(13行),则修改时间戳。使得系统不可能发生二次赠予的情况。消费线程也是类似,每次操作,都使得时间戳加1(36行),使之不可能重复。
执行上述代码,可以得到以下输出:
可以看到,账户只被赠予了一次。
AtomicStampedReference正是这么做的。它内部不仅维护了对象值,还维护了一个时间戳(我这里把它称为时间戳,实际上它可以使任何一个整数,它使用整数来表示状态值)。当AtomicStampedReference对应的数值被修改时,除了更新数据本身外,还必须要更新时间戳。当AtomicStampedReference设置对象值时,对象值以及时间戳都必须满足期望值,写入才会成功。因此,即使对象值被反复读写,写回原值,只要时间戳发生变化,就能防止不恰当的写入。
AtomicStampedReference的几个API在AtomicReference的基础上新增了有关时间戳的信息:
//比较设置参数依次为:期望值写入新值期望时间戳新时间戳 public boolean compareAndSet(V expectedReference,V newReference,int expectedStamp,int newStamp) //获得当前对象引用 public V getReference() //获得当前时间戳 public int getStamp() //设置当前对象引用和时间戳 public void set(V newReference, int newStamp)
有了AtomicStampedReference这个法宝,我们就再也不用担心对象被写坏啦!现在,就让我们使用AtomicStampedReference在修正那个贵宾卡充值的问题的:
public class AtomicStampedReferenceDemo { static AtomicStampedReference<Integer> money = new AtomicStampedReference<Integer>(19, 0); public static void main(String[] args) { //模拟多个线程同时更新后台数据库,为用户充值 for(int i = 0 ; i < 3 ; i++) { final int timestamp=money.getStamp(); new Thread() { public void run() { while(true){ while(true){ Integer m=money.getReference(); if(m<20){ if(money.compareAndSet(m, m+20,timestamp,timestamp+1)){ System.out.println(“余额小于20元,充值成功,余额:”+money.getReference()+”元”); break; } }else{ //System.out.println(“余额大于20元,无需充值”); break ; } } } } }.start(); } //用户消费线程,模拟消费行为 new Thread() { public void run() { for(int i=0;i<100;i++){ while(true){ int timestamp=money.getStamp(); Integer m=money.getReference(); if(m>10){ System.out.println(“大于10元”); if(money.compareAndSet(m, m-10,timestamp,timestamp+1)){ System.out.println(“成功消费10元,余额:”+money.getReference()); break; } }else{ System.out.println(“没有足够的金额”); break; } } try {Thread.sleep(100);} catch (InterruptedException e) {} } } }.start(); } }
第2行,我们使用AtomicStampedReference代替原来的AtomicReference。第6行获得账户的时间戳。后续的赠予操作以这个时间戳为依据。如果赠予成功(13行),则修改时间戳。使得系统不可能发生二次赠予的情况。消费线程也是类似,每次操作,都使得时间戳加1(36行),使之不可能重复。
执行上述代码,可以得到以下输出:
余额小于20元,充值成功,余额:39元 大于10元 成功消费10元,余额:29 大于10元 成功消费10元,余额:19 大于10元 成功消费10元,余额:9 没有足够的金额
可以看到,账户只被赠予了一次。
相关文章推荐
- 【实战Java高并发程序设计 3】带有时间戳的对象引用:AtomicStampedReference
- 【实战Java高并发程序设计 3】带有时间戳的对象引用:AtomicStampedReference
- Java并发26:Atomic系列-带版本戳的原子引用类型AtomicStampedReference与AtomicMarkableReference
- 无锁的对象引用:AtomicReference(转)
- 重构手法20:Change Value to Reference (将值对象改为引用对象)
- 重构手法21:Change Reference to Value (将引用对象改为值对象)
- System.NullReferenceException: 未将对象引用设置到对象的实例。
- System.NullReferenceException: 未将对象引用设置到对象的实例
- 报错:“System.NullReferenceException: 未将对象引用设置到对象的实例”及解决方案(转)
- 周末浅说--未将对象引用设置到对象的实例(System.NullReferenceException)
- 报错:“System.NullReferenceException: 未将对象引用设置到对象的实例”
- 对象的自身引用(Self-Reference) 动态绑定(Dynamic Binding)
- Java引用对象SoftReference WeakReference PhantomReference
- Java对象的强、软、弱和虚引用原理+结合ReferenceQueue对象构造Java对象的高速缓存器
- java对象几种引用(reference)的分析(待后期完善)
- asp.net报错:“System.NullReferenceException: 未将对象引用设置到对象的实例”
- 对象引用与托管指针(object references and managed pointers)
- 关于“System.NullReferenceException: 未将对象引用设置到对象的实例”问题原因。
- Change Reference to Value(将引用对象改变为实值对象)
- C# 调试的时候怎么知道两个Reference引用的是同一个对象