在这一部分开始讨论数组原子操作和一些其他的原子操作。

AtomicIntegerArray/AtomicLongArray/AtomicReferenceArray的API类似,选择有代表性的AtomicIntegerArray来描述这些问题。

int get(int i)

获取位置 i 的当前值。很显然,由于这个是数组操作,就有索引越界的问题(IndexOutOfBoundsException异常)。

对于下面的API起始和AtomicInteger是类似的,这种通过方法、参数的名称就能够得到函数意义的写法是非常值得称赞的。在《重构:改善既有代码的设计》《代码整洁之道》中都非常推崇这种做法。

void set(int i, int newValue)
void lazySet(int i, int newValue)
int getAndSet(int i, int newValue)
boolean compareAndSet(int i, int expect, int update)
boolean weakCompareAndSet(int i, int expect, int update)
int getAndIncrement(int i)
int getAndDecrement(int i)
int getAndAdd(int i, int delta)
int incrementAndGet(int i)
int decrementAndGet(int i)
int addAndGet(int i, int delta)

整体来说,数组的原子操作在理解上还是相对比较容易的,这些API就是有多使用才能体会到它们的好处,而不仅仅是停留在理论阶段。

现在关注字段的原子更新。

AtomicIntegerFieldUpdater<T>/AtomicLongFieldUpdater<T>/AtomicReferenceFieldUpdater<T,V>是基于反射的原子更新字段的值。

相应的API也是非常简单的,但是也是有一些约束的。

(1)字段必须是volatile类型的!在后面的章节中会详细说明为什么必须是volatile,volatile到底是个什么东西。

(2)字段的描述类型(修饰符public/protected/default/private)是与调用者与操作对象字段的关系一致。也就是说调用者能够直接操作对象字段,那么就可以反射进行原子操作。但是对于父类的字段,子类是不能直接操作的,尽管子类可以访问父类的字段。

(3)只能是实例变量,不能是类变量,也就是说不能加static关键字。

(4)只能是可修改变量,不能使final变量,因为final的语义就是不可修改。实际上final的语义和volatile是有冲突的,这两个关键字不能同时存在。

(5)对于AtomicIntegerFieldUpdaterAtomicLongFieldUpdater只能修改int/long类型的字段,不能修改其包装类型(Integer/Long)。如果要修改包装类型就需要使用AtomicReferenceFieldUpdater

在下面的例子中描述了操作的方法。

package xylz.study.concurrency.atomic; 

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; 

public class AtomicIntegerFieldUpdaterDemo { 

   class DemoData{
       public volatile int value1 = 1;
       volatile int value2 = 2;
       protected volatile int value3 = 3;
       private volatile int value4 = 4;
   }
    AtomicIntegerFieldUpdater<DemoData> getUpdater(String fieldName) {
        return AtomicIntegerFieldUpdater.newUpdater(DemoData.class, fieldName);
    }
    void doit() {
        DemoData data = new DemoData();
        System.out.println("1 ==> "+getUpdater("value1").getAndSet(data, 10));
        System.out.println("3 ==> "+getUpdater("value2").incrementAndGet(data));
        System.out.println("2 ==> "+getUpdater("value3").decrementAndGet(data));
        System.out.println("true ==> "+getUpdater("value4").compareAndSet(data, 4, 5));
    }
    public static void main(String[] args) {
        AtomicIntegerFieldUpdaterDemo demo = new AtomicIntegerFieldUpdaterDemo();
        demo.doit();
    }


在上面的例子中DemoData的字段value3/value4对于AtomicIntegerFieldUpdaterDemo类是不可见的,因此通过反射是不能直接修改其值的。

AtomicMarkableReference类描述的一个<Object,Boolean>的对,可以原子的修改Object或者Boolean的值,这种数据结构在一些缓存或者状态描述中比较有用。这种结构在单个或者同时修改Object/Boolean的时候能够有效的提高吞吐量。

AtomicStampedReference类维护带有整数“标志”的对象引用,可以用原子方式对其进行更新。对比AtomicMarkableReference类的<Object,Boolean>,AtomicStampedReference维护的是一种类似<Object,int>的数据结构,其实就是对对象(引用)的一个并发计数。但是与AtomicInteger不同的是,此数据结构可以携带一个对象引用(Object),并且能够对此对象和计数同时进行原子操作。

在后面的章节中会提到“ABA问题”,而AtomicMarkableReference/AtomicStampedReference在解决“ABA问题”上很有用

 

原子操作的使用大概就是这么多,大体来说还算是比较清晰的,在下一个章节中,将对象原子操作进行总结,重点介绍下原子操作的原理和设计思想。

最新文章

  1. [游戏学习28] MFC 时钟
  2. Windows安装Python图像处理库:PIL模块
  3. 如何引用CSS样式表
  4. Android的ProgressBar以及自定义进度条
  5. 一台机器同时运行多个appium实例
  6. 转载收藏之用 - 微信公众平台开发教程(五):使用Senparc.Weixin.MP SDK
  7. C# 引用类型与值类型在编码上的区别
  8. 声明式编程思想和EEPlat
  9. (六十四)iOS的socket实现(C+OC混合实现)
  10. Monolog手册参考
  11. intellij error updating changes svn解决办法
  12. C#中as运算符
  13. c# cbo控件
  14. Django Form ModelForm modelfromset
  15. Ext.util.Format.date与Ext.Date.format区别, 转换时间戳
  16. 17秋 SDN课程 第一次上机作业
  17. golang 六宫格、九宫格头像生成
  18. javascript 简单工厂模式
  19. 带有ZLIB_LIBRARY_DEBUG的FindZLIB.cmake文件
  20. python 小练习12

热门文章

  1. C/C++实现单向循环链表(尾指针,带头尾节点)
  2. H5页面在手机上查看 使用手机浏览自己的web项目
  3. Objective-C 中的 Meta-class 是什么?
  4. linux下怎么安装Go开发环境?linux部署golang
  5. 为什么程序员都不喜欢使用switch,而是大量的 if……else if ?
  6. node环境下安装vue-cli
  7. 01_MyBatis入门
  8. NTT FWT(xor or and) 模板
  9. C++: Basic knowledge Part 1
  10. 关于公式文件.eqn