【实战Java高并发程序设计 3】带有时间戳的对象引用:AtomicStampedReference

【实战Java高并发程序设计1】Java中的指针:Unsafe类
【实战Java高并发程序设计2】无锁的对象引用:AtomicReference
AtomicReference没法解决上述问题的根本是由于对象在修改过程当中,丢失了状态信息。对象值自己与状态被画上了等号。所以,咱们只要可以记录对象在修改过程当中的状态值,就能够很好的解决对象被反复修改致使线程没法正确判断对象状态的问题。数据库

AtomicStampedReference正是这么作的。它内部不只维护了对象值,还维护了一个时间戳(我这里把它称为时间戳,实际上它可使任何一个整数,它使用整数来表示状态值)。当AtomicStampedReference对应的数值被修改时,除了更新数据自己外,还必需要更新时间戳。当AtomicStampedReference设置对象值时,对象值以及时间戳都必须知足指望值,写入才会成功。所以,即便对象值被反复读写,写回原值,只要时间戳发生变化,就能防止不恰当的写入。segmentfault

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在修正那个贵宾卡充值的问题的:并发

01 public class AtomicStampedReferenceDemo {
02 static AtomicStampedReference<Integer> money=new AtomicStampedReference<Integer>(19,0);
03    public staticvoid main(String[] args) {
04        //模拟多个线程同时更新后台数据库,为用户充值
05        for(int i = 0 ; i < 3 ; i++) {
06            final int timestamp=money.getStamp();
07             newThread() {  
08                public void run() { 
09                    while(true){
10                        while(true){
11                             Integerm=money.getReference();
12                             if(m<20){
13                          if(money.compareAndSet(m,m+20,timestamp,timestamp+1)){
14           System.out.println("余额小于20元,充值成功,余额:"+money.getReference()+"元");
15                                     break;
16                                 }
17                             }else{
18                                //System.out.println("余额大于20元,无需充值");
19                                 break ;
20                             }
21                        }
22                    }
23                } 
24            }.start();
25         }
26        
27        //用户消费线程,模拟消费行为
28        new Thread() { 
29             publicvoid run() { 
30                for(int i=0;i<100;i++){
31                    while(true){
32                        int timestamp=money.getStamp();
33                        Integer m=money.getReference();
34                        if(m>10){
35                             System.out.println("大于10元");
36                          if(money.compareAndSet(m, m-10,timestamp,timestamp+1)){
37                       System.out.println("成功消费10元,余额:"+money.getReference());
38                                 break;
39                             }
40                        }else{
41                            System.out.println("没有足够的金额");
42                             break;
43                        }
44                    }
45                    try {Thread.sleep(100);} catch (InterruptedException e) {}
46                 }
47             } 
48        }.start(); 
49    }
50 }

第2行,咱们使用AtomicStampedReference代替原来的AtomicReference。第6行得到帐户的时间戳。后续的赠予操做以这个时间戳为依据。若是赠予成功(13行),则修改时间戳。使得系统不可能发生二次赠予的状况。消费线程也是相似,每次操做,都使得时间戳加1(36行),使之不可能重复。高并发

执行上述代码,能够获得如下输出:spa

余额小于20元,充值成功,余额:39元
大于10元
成功消费10元,余额:29
大于10元
成功消费10元,余额:19
大于10元
成功消费10元,余额:9
没有足够的金额

能够看到,帐户只被赠予了一次。
摘自《实战Java高并发程序设计》一书
bVsQzu线程

相关文章
相关标签/搜索