原子更新基本类型
这个使用案例就略了,相信大家再使用他们已经0阻碍了
原子更新数组
当你操作的共享是个数组的话,就可以用这个很方便解决问题了
public static void main(String[] args) { AtomicIntegerArray atomicArray = new AtomicIntegerArray(5); // 设置指定索引位的数值 atomicArray.set(0, 5); // 也可以通过以下方法设置 (实际上默认值为0,这里加了5) // atomicArray.addAndGet(0, 5); // -- 0表示角标 int current = atomicArray.decrementAndGet(0); System.out.println("current = " + current); }
get(int i):获取数组指定位置的值,并不会改变原来的值
set(int i, int newValue):为数组指定索引位设置一个新值
getAndSet(int i, int newValue):获取数组指定位置的原始值后,用newValue这个新值进行覆盖。
getAndAdd(int i, int delta):获取数组指定索引位的原始值后,为数组指定索引位的值增加delta。那么还有个类似的操作为:addAndGet。
incrementAndGet、decrementAndGet
原子更新引用
使用场景:上面ABA问题有一个非常经典例子,请参加上面
若有类似的使用场景,用对应来存储数据,那么使用这个会非常的方便。例子其实非常简单,这里就不贴出来了,主要介绍一些几个常用的API方法吧:
get()
compareAndSet(V expect, V update):如果当前值与给定的expect相等,(注意是引用相等而不是equals()相等),更新为指定的update值。
.getAndSet(V newValue):原子地设为给定值并返回旧值。
set(V newValue):不管三七二十一,直接把内存里值设置为此值。
原子更新字段
这个可以算是原子更新引用更新引用的一个很好补充。上面根性我们只能全量更新,并且对象的地址都完全变化了。比如我们要更新一个学生的成绩,如果你new一个带有新成绩的Student进来,那就相当于Student对象都变了,显然是不符合我们要求的。
因此java提供了我们针对字段的跟新的原子操作,可谓是一个很好的补充。
当然啦,它使用起来还是稍微有点麻烦的,它是基于反射实现,该字段还不能是private的,且必须被volatile 修饰。
这个在业务上几乎涉及不到,但是在我们框架设计行,还是有可能被适用到的。比如我们内部定义一颗树,可以设计为:
private volatile Node left, right;
因为使用极少,因此有兴趣的朋友可以自己去玩玩,这里就略过吧
JDK8新增
DoubleAccumulator、LongAccumulator、DoubleAdder、LongAdder
今天看到阿里巴巴的手册里面说 ,如果你使用的JDK8和以上版本,建议使用LongAdder代替AotmicLong
受限于文章篇幅,关于他们的使用以及和LongAdder和AotmicLong的性能测试对比,请移步这篇博文专门讲解:【小家java】AtomicLong可以抛弃了,请使用LongAdder代替(或使用LongAccumulator)
悲观锁和乐观锁(Java都提供了对应支持)
为了更好的理解上面的一些操作原理,本文有必要稍带讲解一些悲观锁和乐观锁的概念以及区别
在本文讲解悲观锁和乐观锁,主要代表是synchronized和CAS的区别
悲观锁
悲观锁是一种独占锁,它假设的前提是“冲突一定会发生”,所以处理某段可能出现数据冲突的代码时,这个代码段就要被某个线程独占。而独占意味着“其它即将执行这段代码的其他线程”都将进入“阻塞”/“挂起”状态。
synchronized关键字就是java对于悲观锁的实现。
由于悲观锁的影响下,其他线程都将进入 阻塞/挂起 状态。而我们知道,CPU执行线程状态切换是要耗费相当资源的,这主要涉及到CPU寄存器的操作。所以悲观锁在性能上不会有太多惊艳的表现(但是一般也不至于成为性能瓶颈,所以各位也不要一棒子打死)
乐观锁
乐观锁假定“冲突不一定会出现”,如果出现冲突则进行重试,直到冲突消失。 由于乐观锁的假定条件,所以乐观锁不会独占资源,性能自然在**多数情况下**就会好于悲观锁。
AtomicInteger是一个标准的乐观锁实现,sun.misc.Unsafe是JDK提供的乐观锁的支持。
public final long getAndAddLong(Object var1, long var2, long var4) { long var6; do { var6 = this.getLongVolatile(var1, var2); } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4)); return var6; }
为什么是多数情况呢?因为一旦多线程对某个资源的抢占频度达到了某种规模,就会导致乐观锁内部出现多次更新失败的情况,最终造成乐观锁内部进入一种“活锁”状态。这时乐观锁的性能反而没有悲观锁好。
如果我们很好的理解了乐观锁,并且能很熟练的应用的话,我们可以把它运用到我们项目了,帮助改善性能,比一遇到并发问题就去使用悲观锁的选手,显得更加的NB轰轰了有木有