【实战Java高并发程序设计 5】让普通变量也享受原子操作
【实战Java高并发程序设计 1】Java中的指针:Unsafe类
【实战Java高并发程序设计 2】无锁的对象引用:AtomicReference
【实战Java高并发程序设计 3】带有时间戳的对象引用:AtomicStampedReference
【实战Java高并发程序设计 4】数组也能无锁:AtomicIntegerArray
有时候,由于初期考虑不周,或者后期的需求变化,一些普通变量可能也会有线程安全的需求。如果改动不大,我们可以简单地修改程序中每一个使用或者读取这个变量的地方。但显然,这样并不符合软件设计中的一条重要原则——开闭原则。也就是系统对功能的增加应该是开发的,而对修改应该是相对保守的。而且,如果系统里使用到这个变量的地方特别多,一个一个修改也是一件令人厌烦的事情(况且很多使用场景下可能只是只读的,并无线程安全的强烈要求,完全可以保持原样)。
如果你有这种困扰,在这里根本不需要担心,因为在原子包里还有一个实用的工具类AtomicIntegerFieldUpdater。它可以让你在不改动(或者极少改动)原有代码的基础上,让普通的变量也享受CAS操作带来的线程安全性,这样你可以修改极少的代码,来获得线程安全的保证。这听起来是不是让人很激动呢?
根据数据类型不同,这个Updater有3种,分别是AtomicIntegerFieldUpdater、AtomicLongFieldUpdater和AtomicReferenceFieldUpdater。顾名思义,它们分别可以对int、long和普通对象就行CAS修改。
现在来思考这么一个场景。假设某地要进行一次选举。现在模拟这个机票场景,如果选民投了候选人一票,就记为1,否则记为0。最终的选票显然就是所有数据的简单求和。
public class AtomicIntegerFieldUpdaterDemo {
public static class Candidate{
int id;
volatile int score;
}
public final static AtomicIntegerFieldUpdater<Candidate> scoreUpdater
= AtomicIntegerFieldUpdater.newUpdater(Candidate.class, "score");
//检查Updater是否工作正确
public static AtomicInteger allScore=new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
final Candidate stu=new Candidate();
Thread[] t=new Thread[10000];
for(int i = 0 ; i < 10000 ; i++) {
t[i]=new Thread() {
public void run() {
if(Math.random()>0.4){
scoreUpdater.incrementAndGet(stu);
allScore.incrementAndGet();
}
}
};
t[i].start();
}
for(int i = 0 ; i < 10000 ; i++) { t[i].join();}
System.out.println("score="+stu.score);
System.out.println("allScore="+allScore);
}
}
上述代码模拟了这个计票场景。候选人的得票数量记录在Candidate.score中。注意,它是一个普通的volatile变量。而volatile变量并不是线程安全的。第6~7行定义了AtomicIntegerFieldUpdater实例,用来对Candidate.score进行写入。而后续的allScore我们用来检查AtomicIntegerFieldUpdater的正确性。如果AtomicIntegerFieldUpdater真的保证了线程安全,那么最终Candidate.score和allScore的值必然是相等的。否则,就说明AtomicIntegerFieldUpdater根本没有确保线程安全的写入。第12~21行模拟了计票过程,这里假设有大约60%的人投赞成票,并且投票是随机进行的。第17行使用Updater修改Candidate.score(这里应该是线程安全的),第18行使用AtomicInteger计数,作为参考基准。
大家如果运行这段程序,不难发现,最终的Candidate.score总是和allScore绝对相等。这说明AtomicIntegerFieldUpdater很好地保证了Candidate.score的线程安全。
虽然AtomicIntegerFieldUpdater很好用,但是还是有几个注意事项:
第一,Updater只能修改它可见范围内的变量。因为Updater使用反射得到这个变量。如果变量不可见,就会出错。比如如果score申明为private,就是不可行的。
第二,为了确保变量被正确的读取,它必须是volatile类型的。如果我们原有代码中未申明这个类型,那么简单得申明一下就行,这不会引起什么问题。
第三,由于CAS操作会通过对象实例中的偏移量直接进行赋值,因此,它不支持static字段(Unsafe. objectFieldOffset()不支持静态变量)。
好了,通过AtomicIntegerFieldUpdater,是不是让我们可以更加随心所欲得对系统关键数据进行线程安全地保护呢?
摘自《实战Java高并发程序设计》
【实战Java高并发程序设计 5】让普通变量也享受原子操作的更多相关文章
- 【实战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高并发程序设计 4】数组也能无锁:AtomicIntegerArray
除了提供基本数据类型外,JDK还为我们准备了数组等复合结构.当前可用的原子数组有:AtomicIntegerArray.AtomicLongArray和AtomicReferenceArray,分别表 ...
- 【实战Java高并发程序设计 3】带有时间戳的对象引用:AtomicStampedReference
[实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference AtomicReference无法解决上述问题的根 ...
- 【实战Java高并发程序设计 1】Java中的指针:Unsafe类
是<实战Java高并发程序设计>第4章的几点. 如果你对技术有着不折不挠的追求,应该还会特别在意incrementAndGet() 方法中compareAndSet()的实现.现在,就让我 ...
- 《实战java高并发程序设计》源码整理及读书笔记
日常啰嗦 不要被标题吓到,虽然书籍是<实战java高并发程序设计>,但是这篇文章不会讲高并发.线程安全.锁啊这些比较恼人的知识点,甚至都不会谈相关的技术,只是写一写本人的一点读书感受,顺便 ...
- 《实战Java高并发程序设计》读书笔记
文章目录 第二章 Java并行程序基础 2.1 线程的基本操作 2.1.1 线程中断 2.1.2 等待(wait)和通知(notify) 2.1.3 等待线程结束(join)和谦让(yield) 2. ...
- 《实战Java高并发程序设计》读书笔记三
第三章 JDK并发包 1.同步控制 重入锁:重入锁使用java.util.concurrent.locks.ReentrantLock类来实现,这种锁可以反复使用所以叫重入锁. 重入锁和synchro ...
- 《实战Java高并发程序设计》读书笔记二
第二章 Java并行程序基础 1.线程的基本操作 线程:进程是线程的容器,线程是轻量级进程,是程序执行的最小单位,使用多线程而不用多进程去进行并发程序设计是因为线程间的切换和调度的成本远远的小于进程 ...
随机推荐
- hibernate映射文件
Hibernate的持久化类和关系数据库之间的映射通常是用一个XML文档来定义的.该文档通过一系列XML元素的配置,来将持久化类与数据库表之间建立起一一映射.这意味着映射文档是按照持久化类的定义来创建 ...
- python之路:Day02 --- Python基础2
本节内容 1.列表操作 2.元组操作 3.字符串操作 4.字典操作 5.集合操作 6.文件操作 7.字符编码与转换 一.列表操作 定义列表 names = ['Ming',"Hua" ...
- tp框架之session
系统提供了Session管理和操作的完善支持,全部操作可以通过一个内置的session函数完成,该函数可以完成Session的设置.获取.删除和管理操作. session初始化设置 如果session ...
- JS模块化
一.原始写法 /* 模块就是实现特定功能的一组方法. 只要把不同的函数(以及记录状态的变量)简单地放在一起,就算是一个模块. 上面的函数m1()和m2(),组成一个模块.使用的时候,直接调用就行了. ...
- gcc -Wall -pedantic -ansi(转载)
转载自R-G-Y-CQ的新浪博客 -Wall显示所有的警告信息 -Wall选项可以打开所有类型的语法警告,以便于确定程序源代码是否是正确的,并且尽可能实现可移植性. 对Linux开发人员来讲,GCC给 ...
- ADT + JNI实例
Author: Maddock Date: 2015-07-09 本文简单记录了Android中利用jni开发程序初级教程: 步骤 1 下载安装ADT 2 配置NDK 3 新建安卓工程 4 测试jni ...
- 安装openssl 扩展的时候出现Cannot find config.m4. Make sure that you run '/usr/local/php/bin/phpize' in the top level source directory of the module的解决方法
进入php源码包目录:cd /usr/local/php-5.6.25/ext/openssl 执行命令: cp ./config0.m4 ./config.m4 即可
- 【iOS 使用github上传代码】详解
[iOS 使用github上传代码]详解 一.github创建新工程 二.直接添加文件 三.通过https 和 SSH 操作两种方式上传工程 3.1https 和 SSH 的区别: 3.1.1.前者可 ...
- 贝塞尔曲线(cubic bezier)
对于css3的Transitions,网上很多介绍,相信大家都比较了解,这里用最简单的方式介绍下: transition语法:transition:<transition-property> ...
- iOS之转场动画
CodeApp地址CATransition_Demo:https://github.com/Wheat-Qin/CATransition_Demo iOS中比较全的翻转过渡动画(不只是苹果提供的).包 ...