J.U.C-Atomic

前提知识储备:Unsafe

一、并发包下的原子类型5种

并发包下的原子类

  • 1)布尔类型的AtomicBoolean
  • 2)整型AtomicInteger、AtomicIntegerArray、AtomicIntegerFieldUpdater
  • 3)长整型AtomicLong、AtomicLongArray、AtomicLongFieldUpdater
  • 4)引用型AtomicMarkableReference、AtomicReference、AtomicReferenceArray、AtomicReferenceFieldUpdater、AtomicStampedReference
  • 5)累加器DoubleAccumulator、DoubleAdder、LongAccumulator、LongAdder、Striped64

二、并发包下的原子类型5种分为4类

java.util.concurrent.atomic中的类可以分成4组:
标量类(Scalar):AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference
数组类:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
更新器类:AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater
复合变量类:AtomicMarkableReference,AtomicStampedReference

2.1、原子性基本类

  • 第一组AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference这四种基本类型用来处理布尔,整数,长整数,对象四种数据,其内部实现不是简单的使用synchronized,而是一个更为高效的方式CAS (compare and swap) + volatile和native方法,从而避免了synchronized的高开销,执行效率大为提升。如AtomicInteger的实现片断为
    在这里插入图片描述
    compareAndSet( ) 和weakCompareAndSet( )方法
    这 两个方法都是conditional modifier方法。这2个方法接受2个参数,一个是期望数据(expected),一个是新数据(new);如果atomic里面的数据和期望数据一 致,则将新数据设定给atomic的数据,返回true,表明成功;否则就不设定,并返回false。JSR规范中说:以原子方式读取和有条件地写入变量但不 创建任何 happen-before 排序,因此不提供与除 weakCompareAndSet 目标外任何变量以前或后续读取或写入操作有关的任何保证。大意就是说调用weakCompareAndSet时并不能保证不存在happen- before的发生(也就是可能存在指令重排序导致此操作失败)。但是从Java源码来看,其实此方法并没有实现JSR规范的要求,最后效果和 compareAndSet是等效的,都调用了unsafe.compareAndSwapInt()完成操作。
    在这里插入图片描述
    对于 AtomicInteger、AtomicLong还提供了一些特别的方法。
  • getAndIncrement( ):以原子方式将当前值加 1,相当于线程安全的i++操作。
  • incrementAndGet( ):以原子方式将当前值加 1, 相当于线程安全的++i操作。
  • getAndDecrement( ):以原子方式将当前值减 1, 相当于线程安全的i–操作。
  • decrementAndGet ( ):以原子方式将当前值减 1,相当于线程安全的–i操作。
  • addAndGet( ): 以原子方式将给定值与当前值相加, 实际上就是等于线程安全的i =i+delta操作。
  • getAndAdd( ):以原子方式将给定值与当前值相加, 相当于线程安全的t=i;i+=delta;return t;操作。

虽然原子的标量类扩展了Number类,但并没有扩展一些基本类型的包装类,如Integer或Long,事实上他们也不能扩展:基本类型的包装类是不可以修改的,而原子变量类是可以修改的。在原子变量类中没有重新定义hashCode或equals方法,每个实例都是不同的,他们也不宜用做基于散列容器中的键值。

2.2、原子性数组元素

  • 第二组AtomicIntegerArray,AtomicLongArray还有AtomicReferenceArray类进一步扩展了原子操作,对这些类型的数组提供了支持。这些类在为其数组元素提供 volatile 访问语义方面也引人注目,这对于普通数组来说是不受支持的。他们内部并不是像AtomicInteger一样维持一个valatile变量,而是全部由native方法实现,如下
    在这里插入图片描述

2.3、反射修改工具

2.3.1、为什么需要这些类

类是自己编写的可以使用原子性类或者原子性数组来定义,但是类不是自己写的,不能更改其源码的条件下,需要实现对其成员变量的原子操作。就需要他们来实现。通过newUpdater静态函数传入要修改的类和对应的成员变量的名字,内部通过反射拿到这个类的成员变量,然后包装成一个AtomicReferenceFieldUpdater对象。如果要修改这个对象的成员变量,那么再传入相应的对象使用方式就跟原子性类型的一样了。

2.3.2、使用要求

  • 第三组AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater基于反射的实用工具,可以对指定类的指定 volatile 字段进行原子更新。API非常简单,但是也是有一些约束:
    -(1)字段必须是volatile类型的
  • (2)字段的描述类型(修饰符public/protected/default/private)是与调用者与操作对象字段的关系一致。也就是说 调用者能够直接操作对象字段,那么就可以反射进行原子操作。但是对于父类的字段,子类是不能直接操作的,尽管子类可以访问父类的字段。
  • (3)只能是实例变量,不能是类变量,也就是说不能加static关键字。
    -(4)只能是可修改变量,不能使final变量,因为final的语义就是不可修改。实际上final的语义和volatile是有冲突的,这两个关键字不能同时存在。
  • (5)对于AtomicIntegerFieldUpdater 和AtomicLongFieldUpdater 只能修改int/long类型的字段,不能修改其包装类型(Integer/Long)。如果要修改包装类型就需要使用AtomicReferenceFieldUpdater

netty5.0中类ChannelOutboundBuffer统计发送的字节总数,由于使用volatile变量已经不能满足,所以使用AtomicIntegerFieldUpdater 来实现的,看下面代码:
在这里插入图片描述

对于CAS的锁优化

  • 5)累加器DoubleAccumulator、DoubleAdder、LongAccumulator、LongAdder、Striped64
    参考这里
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小诚信驿站

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值