返回介绍

7.1 原子更新基本类型类

发布于 2024-08-18 10:09:30 字数 2664 浏览 0 评论 0 收藏 0

使用原子的方式更新基本类型,Atomic包提供了以下3个类。

AtomicBoolean:原子更新布尔类型。

AtomicInteger:原子更新整型。

AtomicLong:原子更新长整型。

以上3个类提供的方法几乎一模一样,所以本节仅以AtomicInteger为例进行讲解,AtomicInteger的常用方法如下。

int addAndGet(int delta):以原子方式将输入的数值与实例中的值(AtomicInteger里的value)相加,并返回结果。

boolean compareAndSet(int expect,int update):如果输入的数值等于预期值,则以原子方式将该值设置为输入的值。

int getAndIncrement():以原子方式将当前值加1,注意,这里返回的是自增前的值。

void lazySet(int newValue):最终会设置成newValue,使用lazySet设置值后,可能导致其他线程在之后的一小段时间内还是可以读到旧的值。关于该方法的更多信息可以参考并发编程网翻译的一篇文章《AtomicLong.lazySet是如何工作的?》,文章地址是“http://ifeve.com/how-does-atomiclong-lazyset-work/”。

int getAndSet(int newValue):以原子方式设置为newValue的值,并返回旧值。

AtomicInteger示例代码如代码清单7-1所示。

代码清单7-1 AtomicIntegerTest.java

import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntegerTest {
  static AtomicInteger ai = new AtomicInteger(1);
  public static void main(String[] args) {
    System.out.println(ai.getAndIncrement());
    System.out.println(ai.get());
  }
}

输出结果如下。

1
2

那么getAndIncrement是如何实现原子操作的呢?让我们一起分析其实现原理,getAndIncrement的源码如代码清单7-2所示。

代码清单7-2 AtomicInteger.java

public final int getAndIncrement() {
  for (;;) {
      int current = get();
      int next = current + 1;
      if (compareAndSet(current, next))
    return current;
  }
}
public final boolean compareAndSet(int expect, int update) {
  return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

源码中for循环体的第一步先取得AtomicInteger里存储的数值,第二步对AtomicInteger的当前数值进行加1操作,关键的第三步调用compareAndSet方法来进行原子更新操作,该方法先检查当前数值是否等于current,等于意味着AtomicInteger的值没有被其他线程修改过,则将AtomicInteger的当前数值更新成next的值,如果不等compareAndSet方法会返回false,程序会进入for循环重新进行compareAndSet操作。

Atomic包提供了3种基本类型的原子更新,但是Java的基本类型里还有char、float和double等。那么问题来了,如何原子的更新其他的基本类型呢?Atomic包里的类基本都是使用Unsafe实现的,让我们一起看一下Unsafe的源码,如代码清单7-3所示。

代码清单7-3 Unsafe.java

/**
 * 如果当前数值是expected,则原子的将Java变量更新成x
 * @return 如果更新成功则返回true
 */
public final native boolean compareAndSwapObject(Object o,
             long offset,
             Object expected,
             Object x);
public final native boolean compareAndSwapInt(Object o, long offset,
                int expected,
                int x);
public final native boolean compareAndSwapLong(Object o, long offset,
                 long expected,
                 long x);

通过代码,我们发现Unsafe只提供了3种CAS方法:compareAndSwapObject、compare-AndSwapInt和compareAndSwapLong,再看AtomicBoolean源码,发现它是先把Boolean转换成整型,再使用compareAndSwapInt进行CAS,所以原子更新char、float和double变量也可以用类似的思路来实现。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文