java concurrent 原子_深入理解java:2.3.1. 并发编程concurrent包 之Atomic原子操作(循环CAS)...

java中,可能有一些场景,操作非常简单,但是容易存在并发问题,比如i++,

此时,如果依赖锁机制,可能带来性能损耗等问题,

于是,如何更加简单的实现原子性操作,就成为java中需要面对的一个问题。

在backport-util-concurrent没有被引入java1.5并成为JUC之前,

这些原子类和原子操作方法,都是使用synchronized实现的。

不过JUC出现之后,这些原子操作 基于JNI提供了新的实现,

比如AtomicInteger,AtomicLong,AtomicBoolean,AtomicReference,AtomicIntegerArray/AtomicLongArray/AtomicReferenceArray;

这些操作中提供一些原子化操作,比如incrementAndGet(相当于i++),compareAndSet(安全赋值)等,直接读源代码也很容易懂。

以AtomicInteger为例,看看它是怎么做到的:

如果是读取值,很简单,将value声明为volatile的,就可以保证在没有锁的情况下,数据是线程可见的:

1 private volatile int value;

public final intget() {

2 returnvalue;

3 }

那么,涉及到值变更的操作呢?以AtomicInteger实现:++i为例:

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 public final intincrementAndGet() {

2 for(;;) {

3 int current =get();

4 int next = current + 1;

5 if(compareAndSet(current, next))

6 returnnext;

7 }

8 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

在这里采用了CAS操作,每次从内存中读取数据然后将此数据和+1后的结果进行CAS操作,如果成功就返回结果,否则重试直到成功为止。

而这里的comparAndSet(current,next),就是前面介绍CAS的时候所说的依赖JNI实现的乐观锁做法:

public final boolean compareAndSet(int expect, intupdate) {

return unsafe.compareAndSwapInt(this, valueOffset, expect, update);

}

数组原子化

注意,Java中Atomic*Array,并不是对整个数组对象实现原子化(也没有必要这样做),而是对数组中的某个元素实现原子化。

例如,对于一个整型原子数组,其中的原子方法,都是对每个元素的:

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 public final int getAndDecrement(inti) {

2 while (true) {

3 int current =get(i);

4 int next = current - 1;

5 if(compareAndSet(i, current, next))

6 returncurrent;

7 }

8 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

引用的原子化操作

引用的操作本身不就是原子的吗?

一个对象的引用,从A切换到B,本身也不会出现 非原子操作啊?这种想法本身没有什么问题,

但是考虑下嘛的场景:对象a,当前执行引用a1,

线程X期望将a的引用设置为a2,也就是a=a2,

线程Y期望将a的引用设置为a3,也就是a=a3。

X要求,a必须从a1变为a2,也就是说compareAndSet(expect=a1,setValue=a2);

Y要求,a必须从a1变为a3,也就是说compareAndSet(expect=a1,setValue=a3)。

如果严格遵循要求,应该出现X把a的引用设置为a2后,Y线程操作失败的情况,也就是说:

X:a==a1--> a=a2;

Y:a!=a1 --> Exception;

如果没有原子化,那么Y会直接将a赋值为a3,从而导致出现脏数据。

这就是原子引用AtomicReference存在的原因。

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 public finalV getAndSet(V newValue) {

2 while (true) {

3 V x =get();

4 if(compareAndSet(x, newValue))

5 returnx;

6 }

7 }

48304ba5e6f9fe08f3fa1abda7d326ab.png

注意,AtomicReference要求引用也是volatile的。

Updater原子化

其它几个Atomic类,都是对被volatile修饰的基本数据类型的自身数据进行原子化操作,

但是如果一个被volatile修饰的变量本身已经存在在类中,那要如何提供原子化操作呢?

比如,一个Person,其中有个属性为age,private volatile int age,

如何对age提供原子化操作呢?

1 private AtomicIntegerFieldUpdater updater = AtomicIntegerFieldUpdater.newUpdater(Person.class, "age");

2 updater.getAndIncrement(5);//加5岁

3 updater.compareAndSet(person, 30, 35)//如果一个人的年龄是30,设置为35。

Java中的Atomic包使用指南

参考: http://ifeve.com/java-atomic/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值