资讯专栏INFORMATION COLUMN

猫头鹰的深夜翻译:Java中的CAS(Compare And Swap)

hosition / 885人阅读

摘要:否则它就会用新的值替代当前值。在这种情况下,锁可能会优于原子变量,但在实际的争用级别中,原子变量的性能优于锁。在中引入了另外一个构件。

题目要求

在我们深入了解CAS(Compare And Swap)策略以及它是如何在AtomicInteger这样的原子构造器中使用的,首先来看一下这段代码:

public class MyApp
{
    private volatile int count = 0;
    public void upateVisitors() 
    {
       ++count; //increment the visitors count
    }
}

这里的代码记录的访问应用的访客的数量。这段代码有问题么?如果多个线程试图更新这个数值会发生什么?事实上,这里的问题在于单单将count标记为volatile并不能保证原子性,++count也不是一个原子操作。想要了解更多请查看这里。

那么如果我们将方法标记为synchronized可以解决这个问题吗?

public class MyApp
{
    private int count = 0;
    public synchronized void upateVisitors() 
    {
       ++count; //increment the visitors count
    }
}

这段代码可以保证原子性吗?可以。
这段代码可以保证可见性啊?可以。

那这里还有什么问题?

它使用了锁从而引入了大量的延时和。详情查看这里。这种方式开销太大。

为了解决这个问题引入了原子构造器。如果我们使用AtomicInteger来记录访问量,也可以达到目的。

public class MyApp
{
    private AtomicInteger count = new AtomicInteger(0);
    public void upateVisitors() 
    {
       count.incrementAndGet(); //increment the visitors count
    }
}

支持原子操作的类如AtomicInteger,使用CAS来实现。CAS并没有使用锁,而是以一种很乐观的方式来处理。它遵循以下几步:

比较原始的值和我们已经获得的值

如果这两个值不同,说明中间有线程改变了值。否则它就会用新的值替代当前值。

看一下AtomicLong类中的代码:

public final long incrementAndGet() {
    for (;;) {
        long current = get();
        long next = current + 1;
        if (compareAndSet(current, next))
          return next;
    }
}

在JDK 8中上面的代码被更改为一行代码:

public final long incrementAndGet() {
        return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
}

这一行代码有何优点?

实际上,这一行是会由JIT翻译为优化的指令序列的JVM内部函数。在x86架构中它就是一条CPU指令LOCK XADD,会比CAS循环的性能好很多。

现在考虑一下当我们有较高的争用以及一些线程想要更​​新相同的原子变量的可能性。在这种情况下,锁可能会优于原子变量,但在实际的争用级别中,原子变量的性能优于锁。在Java 8 中引入了另外一个构件LongAdder

LongAdder并不完全是AtomicLong的替代品,我们需要考虑以下因素:

当没有争用时,AtomicLong性能更好

LongAdder将分配Cells(在抽象类Striped64中声明的final类)以避免消耗内存的争用。所以如果我们有一个紧张的内存预算,我们应该更倾向于使用AtomicLong。


想要了解更多开发技术,面试教程以及互联网公司内推,欢迎关注我的微信公众号!将会不定期的发放福利哦~

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/68794.html

相关文章

  • 头鹰深夜翻译:核心JAVA并发(一)

    摘要:简介从创建以来,就支持核心的并发概念如线程和锁。这篇文章会帮助从事多线程编程的开发人员理解核心的并发概念以及如何使用它们。请求操作系统互斥,并让操作系统调度程序处理线程停放和唤醒。 简介 从创建以来,JAVA就支持核心的并发概念如线程和锁。这篇文章会帮助从事多线程编程的JAVA开发人员理解核心的并发概念以及如何使用它们。 (博主将在其中加上自己的理解以及自己想出的例子作为补充) 概念 ...

    Richard_Gao 评论0 收藏0
  • CAS 算法 —— Compare and Swap

    摘要:算法算法会先对一个内存变量位置和一个给定的值进行比较,如果相等,则用一个新值去修改这个内存变量位置。因为是非公平锁,所以一上来就尝试抢占锁给定旧值并希望用新值去更新内存变量。 本文翻译和原创各占一半,所以还是厚颜无耻归类到原创好了...https://howtodoinjava.com/jav...java 5 其中一个令人振奋的改进是新增了支持原子操作的类型,例如 AtomicInt...

    mmy123456 评论0 收藏0
  • 头鹰深夜翻译:Volatile原子性, 可见性和有序性

    摘要:有可能一个线程中的动作相对于另一个线程出现乱序。当实际输出取决于线程交错的结果时,这种情况被称为竞争条件。这里的问题在于代码块不是原子性的,而且实例的变化对别的线程不可见。这种不能同时在多个线程上执行的部分被称为关键部分。 为什么要额外写一篇文章来研究volatile呢?是因为这可能是并发中最令人困惑以及最被误解的结构。我看过不少解释volatile的博客,但是大多数要么不完整,要么难...

    Lionad-Morotar 评论0 收藏0
  • 头鹰深夜翻译Java 2D Graphics, 简单仿射变换

    摘要:什么是仿射变换一组设备无关的坐标被用来将所有的坐标信息传递给对象。对象作为对象状态的一部分。类代表一个的仿射变化,将一组的坐标进行线性映射到另一组保留了平行关系和竖直关系的坐标中。 什么是仿射变换 一组设备无关的坐标被用来将所有的坐标信息传递给Graphics2D对象。AffineTransform对象作为Graphics2D对象状态的一部分。该对象定义了如何将用户空间的坐标转化为设备...

    whinc 评论0 收藏0
  • 头鹰深夜翻译Java WeakHashMap

    摘要:本文简介类概览类构造器总结类构造方法类使用举例类概览是一个实现了接口,并且键为型的哈希表。中的条目不再被正常使用时,会被自动删除。它的键值均支持。和绝大多数的集合类一样,这个类不是同步的。 本文简介 WeakHashMap类概览 WeakHashMap类构造器总结 WeakHashMap类构造方法 WeakHasjMap类使用举例 1. WeakHashMap类概览 Wea...

    BothEyes1993 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<