【实战Java高并发程序设计 3】带有时间戳的对象引用:AtomicStampedReference
【实战Java高并发程序设计 1】Java中的指针:Unsafe类
【实战Java高并发程序设计 2】无锁的对象引用:AtomicReference
AtomicReference无法解决上述问题的根本是因为对象在修改过程中,丢失了状态信息。对象值本身与状态被画上了等号。因此,我们只要能够记录对象在修改过程中的状态值,就可以很好的解决对象被反复修改导致线程无法正确判断对象状态的问题。
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在修正那个贵宾卡充值的问题的:
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行),使之不可能重复。
执行上述代码,可以得到以下输出:
余额小于20元,充值成功,余额:39元
大于10元
成功消费10元,余额:29
大于10元
成功消费10元,余额:19
大于10元
成功消费10元,余额:9
没有足够的金额
可以看到,账户只被赠予了一次。
【实战Java高并发程序设计 1】Java中的指针:Unsafe类
【实战Java高并发程序设计 2】无锁的对象引用:AtomicReference
摘自:实战Java高并发程序设计
【实战Java高并发程序设计 3】带有时间戳的对象引用:AtomicStampedReference的更多相关文章
- 【实战Java高并发程序设计 7】让线程之间互相帮助--SynchronousQueue的实现
[实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference [实战Java高并发程序设计 3]带有时间戳的对象 ...
- 【实战Java高并发程序设计6】挑战无锁算法:无锁的Vector实现
[实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference [实战Java高并发程序设计 3]带有时间戳的对象 ...
- 【实战Java高并发程序设计 5】让普通变量也享受原子操作
[实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference [实战Java高并发程序设计 3]带有时间戳的对象 ...
- 《实战Java高并发程序设计》读书笔记
文章目录 第二章 Java并行程序基础 2.1 线程的基本操作 2.1.1 线程中断 2.1.2 等待(wait)和通知(notify) 2.1.3 等待线程结束(join)和谦让(yield) 2. ...
- 【实战Java高并发程序设计 4】数组也能无锁:AtomicIntegerArray
除了提供基本数据类型外,JDK还为我们准备了数组等复合结构.当前可用的原子数组有:AtomicIntegerArray.AtomicLongArray和AtomicReferenceArray,分别表 ...
- 【实战Java高并发程序设计 1】Java中的指针:Unsafe类
是<实战Java高并发程序设计>第4章的几点. 如果你对技术有着不折不挠的追求,应该还会特别在意incrementAndGet() 方法中compareAndSet()的实现.现在,就让我 ...
- 《实战java高并发程序设计》源码整理及读书笔记
日常啰嗦 不要被标题吓到,虽然书籍是<实战java高并发程序设计>,但是这篇文章不会讲高并发.线程安全.锁啊这些比较恼人的知识点,甚至都不会谈相关的技术,只是写一写本人的一点读书感受,顺便 ...
- 《实战Java高并发程序设计》读书笔记三
第三章 JDK并发包 1.同步控制 重入锁:重入锁使用java.util.concurrent.locks.ReentrantLock类来实现,这种锁可以反复使用所以叫重入锁. 重入锁和synchro ...
- 《实战Java高并发程序设计》读书笔记二
第二章 Java并行程序基础 1.线程的基本操作 线程:进程是线程的容器,线程是轻量级进程,是程序执行的最小单位,使用多线程而不用多进程去进行并发程序设计是因为线程间的切换和调度的成本远远的小于进程 ...
随机推荐
- C#使用ListView更新数据出现闪烁解决办法
C#使用ListView更新数据出现闪烁解决办法 在使用vs自动控件ListView控件时候,更新里面的部分代码时候出现闪烁的情况 如图: 解决以后: 解决办法使用双缓冲:添加新类继承ListView ...
- JS的双等和三等的区别
在JS会碰到一个奇怪的运算符"===",之前学JAVA和C语言,只有"==",JS冒出个"===",挺奇怪的,遂google之... 在JS ...
- thinkphp验证是否登录并跳转
CommonController.class.php <?php namespace Admin\Controller; use Think\Controller; class CommonCo ...
- prototype 和__proto__
//Animal构造函数 function Animal(name){ this.name = name; } //Animal原型对象 Animal.prototype = { id:"A ...
- Good Bye 2016 - B
题目链接:http://codeforces.com/contest/750/problem/B 题意:地球的子午线长度为40000,两极点的距离为20000.现在你从北极出发,按照题目输入方式来走. ...
- iOS之关于开发的那点破事(一)
前言: 前段时间,经理突然找我说:能不能在项目中对缓存的图片进行加密?当时就感到疑惑,就说:可以是可以,但为什么要这样做?有什么意义没? 我们都知道,apple使用的沙盒(sandbox)机制,这种机 ...
- 代码中使用StoryBoard和DoubleAnimation的方法
TranslateTransformを対象に.DoubleAnimation型のアニメーションを使用して.TranslateTransform.Xプロパティを ”-1 * Imageコントロールの幅” ...
- div高度根据内容自动增大
1.很多时候我们希望容器高度能够自适应内部元素的变化,需要用到min-height属性. 2.有时候用了min-height还是不会随着内容自适应高度,您需要检查下容器的子元素是不是有浮动属性,当子元 ...
- 深入理解JavaScript运行机制
深入理解JavaScript运行机制 前言 本文是写作在给团队新人培训之际,所以其实本文的受众是对JavaScript的运行机制不了解或了解起来有困难的小伙伴.也就是说,其实真正的原理和本文阐述的并不 ...
- 浅谈ajax
Ajax 回顾 最本质的 ajax 其实是这样的: function Ajax(){ var xmlHttpReq = null; if (window.ActiveXObject){//IE5 IE ...