【实战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的更多相关文章

  1. 【实战Java高并发程序设计 7】让线程之间互相帮助--SynchronousQueue的实现

    [实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference [实战Java高并发程序设计 3]带有时间戳的对象 ...

  2. 【实战Java高并发程序设计6】挑战无锁算法:无锁的Vector实现

    [实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference [实战Java高并发程序设计 3]带有时间戳的对象 ...

  3. 【实战Java高并发程序设计 5】让普通变量也享受原子操作

    [实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference [实战Java高并发程序设计 3]带有时间戳的对象 ...

  4. 《实战Java高并发程序设计》读书笔记

    文章目录 第二章 Java并行程序基础 2.1 线程的基本操作 2.1.1 线程中断 2.1.2 等待(wait)和通知(notify) 2.1.3 等待线程结束(join)和谦让(yield) 2. ...

  5. 【实战Java高并发程序设计 4】数组也能无锁:AtomicIntegerArray

    除了提供基本数据类型外,JDK还为我们准备了数组等复合结构.当前可用的原子数组有:AtomicIntegerArray.AtomicLongArray和AtomicReferenceArray,分别表 ...

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

    是<实战Java高并发程序设计>第4章的几点. 如果你对技术有着不折不挠的追求,应该还会特别在意incrementAndGet() 方法中compareAndSet()的实现.现在,就让我 ...

  7. 《实战java高并发程序设计》源码整理及读书笔记

    日常啰嗦 不要被标题吓到,虽然书籍是<实战java高并发程序设计>,但是这篇文章不会讲高并发.线程安全.锁啊这些比较恼人的知识点,甚至都不会谈相关的技术,只是写一写本人的一点读书感受,顺便 ...

  8. 《实战Java高并发程序设计》读书笔记三

    第三章 JDK并发包 1.同步控制 重入锁:重入锁使用java.util.concurrent.locks.ReentrantLock类来实现,这种锁可以反复使用所以叫重入锁. 重入锁和synchro ...

  9. 《实战Java高并发程序设计》读书笔记二

    第二章  Java并行程序基础 1.线程的基本操作 线程:进程是线程的容器,线程是轻量级进程,是程序执行的最小单位,使用多线程而不用多进程去进行并发程序设计是因为线程间的切换和调度的成本远远的小于进程 ...

随机推荐

  1. Lintcode 157. 判断字符串是否没有重复字符

    ------------------------ 因为字符究竟是什么样的无法确定(比如编码之类的),恐怕是没办法假设使用多大空间(位.数组)来标记出现次数的,集合应该可以但感觉会严重拖慢速度... 还 ...

  2. hdu 2191 珍惜现在,感恩生活

    链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=2191 思路:多重背包模板题 #include <stdio.h> #include ...

  3. 比较完整的WebView的用法

    WebView, WebChromeClient和WebViewClient加载网页基本用法 webview是android中的浏览器控件,在一些手机应用中常会用到b/s模式去开发应用,这时webvi ...

  4. 第三十二篇:在SOUI2.0中像android一样使用资源

    SOUI2.0之前,在SOUI中使用资源通常是直接使用这个资源的name(一个字符串)来引用.使用字符串的好处在于字符串能够表达这个资源的意义,因此使用字符串也是现代UI引擎常用的方式. 尽管直接使用 ...

  5. Learn Git and GitHub without any code!

    What is GitHub? GitHub is a code hosting platform for version control and collaboration.代码托管平台. repo ...

  6. IDEA快捷键+使用小技巧

    一 常用快捷键 Alt+回车 导入包,自动修正,当引入的类需要异常捕获的时候 Ctrl+Shift+Space 自动补全代码,"new"字符,还可以引入强制转换的 Ctrl-Alt ...

  7. 多个jar包合并成一个jar包的办法

    步骤: 1.将多个JAR包使用压缩软件打开,并将全包名的类拷贝到一个临时目录地下. 2.cmd命令到该临时目录下,此时会有很多.class文件,其中需要带完整包路径 3.执行 jar -cvfM te ...

  8. Domino----The Address Book does not contain a cross certificate capable of validating the public key.

    The Address Book does not contain a cross certificate capable of validating the public key. 地址本不包含交叉 ...

  9. Fiddler问题 - creation of the root certificate was not successful

    打开cmd执行命令. d: cd D:\soft\Fiddler2 makecert.exe -r -ss my -n "CN=DO_NOT_TRUST_FiddlerRoot, O=DO_ ...

  10. CentOS安装JDK和安装Glassfish

    1.首先下载对应CentOS版本的jdk:下载:jdk-7u75-linux-x64.tar.gz 2.下载该jdk到本地,并上传到CentOS系统的opt临时目录下 3.在安装自己下载的jdk之前, ...