【实战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.线程的基本操作 线程:进程是线程的容器,线程是轻量级进程,是程序执行的最小单位,使用多线程而不用多进程去进行并发程序设计是因为线程间的切换和调度的成本远远的小于进程 ...
随机推荐
- Servlet引擎Jetty之入门1
Jetty与tomcat一样,HttpWeb容器,支持实现Servlet规范. 详细介绍参考:https://www.ibm.com/developerworks/cn/java/j-lo-jetty ...
- 关于控件的Invoke(...)方法和BeginInvoke(...)方法的区别
这两个方法最主要的区别就是一个是同步,一个是异步,即会阻塞线程,那么阻塞哪个线程呢?我们用代码来分析(工具是VS2010) using System; using System.Collections ...
- 搭建LAMP环境注意事项
一:安装mysql 5.5以上版本需要使用cmake 和 bison 并且需要安装ncurses 在安装MySQL完毕之后,需要覆盖 掉 /etc/my.cnf centos默认会有一个my.cnf文 ...
- notepad++快捷键
notepad++现在是我最常用的文本编辑工具,其中使用的列模式编辑,也是很好使用的. 基本的快捷键: Ctrl-C,Ctrl-X,Ctrl-V,Ctrl-Y,Ctrl-A,Ctrl-F,Ctrl-S ...
- 多线程之互斥锁(By C++)
首先贴一段win32API实现的多线程的代码,使用CreateThread实现,如果不要传参数,就把第四个参数设为NULL #include<Windows.h> #include< ...
- LINQ 左右连接
LINQ 左右连接:DefaultIfEmpty() incomeList = (from p in db.Incomes join m in db.Items on p.ItemID equals ...
- Xcode playground markdown常用语法
//: **Bold** *Italic* /*: # h1 ## h2 ### h3 #### h4 h5 h6 same as h3 --- --- --- * blank seperate li ...
- [BZOJ1997][HNOI2010] 平面图判定
Description Input Output 是的..BZOJ样例都没给. 题解(from 出题人): 如果只考虑简单的平面图判定,这个问题是非常不好做的. 但是题目中有一个条件— ...
- 【读书笔记】《编程珠玑》第一章之位向量&位图
此书的叙述模式是借由一个具体问题来引出的一系列算法,数据结构等等方面的技巧性策略.共分三篇,基础,性能,应用.每篇涵盖数章,章内案例都非常切实棘手,解说也生动有趣. 自个呢也是头一次接触编程技巧类的书 ...
- swift-Array(数组)
Swift 数组用于存储相同类型的值的顺序列表.Swift 要严格检查,不允许不同类型的值在同一个数组中 声明一个数组 var someArray = [SomeType]() var someArr ...