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

2 篇文章 0 订阅

【实战Java高并发程序设计 1】Java中的指针:Unsafe类

【实战Java高并发程序设计 2】无锁的对象引用:AtomicReference

AtomicReference无法解决上述问题的根本是因为对象在修改过程中,丢失了状态信息。对象值本身与状态被画上了等号。因此,我们只要能够记录对象在修改过程中的状态值,就可以很好的解决对象被反复修改导致线程无法正确判断对象状态的问题。

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

AtomicStampedReference的几个API在AtomicReference的基础上新增了有关时间戳的信息:

[java]  view plain  copy
  1. //比较设置 参数依次为:期望值 写入新值 期望时间戳 新时间戳  
  2. public boolean compareAndSet(V expectedReference,V    
  3. newReference,int expectedStamp,int newStamp)  
  4. //获得当前对象引用  
  5. public V getReference()  
  6. //获得当前时间戳  
  7. public int getStamp()  
  8. //设置当前对象引用和时间戳  
  9. public void set(V newReference, int newStamp)  

有了AtomicStampedReference这个法宝,我们就再也不用担心对象被写坏啦!现在,就让我们使用AtomicStampedReference在修正那个贵宾卡充值的问题的:

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

第2行,我们使用AtomicStampedReference代替原来的AtomicReference。第6行获得账户的时间戳。后续的赠予操作以这个时间戳为依据。如果赠予成功(13行),则修改时间戳。使得系统不可能发生二次赠予的情况。消费线程也是类似,每次操作,都使得时间戳加1(36行),使之不可能重复。

执行上述代码,可以得到以下输出:

[html]  view plain  copy
  1. 余额小于20元,充值成功,余额:39元  
  2. 大于10元  
  3. 成功消费10元,余额:29  
  4. 大于10元  
  5. 成功消费10元,余额:19  
  6. 大于10元  
  7. 成功消费10元,余额:9  
  8. 没有足够的金额  
  9. 可以看到,账户只被赠予了一次。  


https://blog.csdn.net/xinaij/article/details/50115511


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值