zoukankan      html  css  js  c++  java
  • JDK源码分析-AtomicInteger

      AtomicInteger可以看做Integer类的原子操作工具类。在java.util.concurrent.atomic包下,在一些使用场合下可以取代加锁操作提高并发性。接下来就从几个方面来介绍:

      1.原子性和CAS。

      2.CPU底层实现原理。

      3.atomic包介绍。

      4.源码分析。

      原子性和CAS

      原子性就是指某一个操作是不可拆分的,是一个整体必须要一次性全部执行完成要么就不执行。

      CAS是Compare And Swap(比较并交换)。意思是当你要更新某个值的时候先要检查这个变量的当前值是不是改变了,如果改变了就不能更新,没改变就可以更新。

      首先,CPU 会将内存中将要被更改的数据与期望的值做比较。然后,当这两个值相等时,CPU 才会将内存中的数值替换为新的值。否则便不做操作。最后,CPU 会将旧的数值返回。这一系列的操作是原子的。它们虽然看似复杂,但却是 Java 5 并发机制优于原有锁机制的根本。简单来说,CAS 的含义是“我认为原有的值应该是什么,如果是,则将原有的值更新为新值,否则不做修改,并告诉我原来的值是多少。

      atomic包介绍

      类的小工具包,支持在单个变量上解除锁的线程安全编程。此包中的类可将 volatile 值、字段和数组元素的概念扩展到那些也提供原子条件更新操作的类,其形式如下:

    boolean compareAndSet(expectedValue, updateValue);
    

      如果此方法(在不同的类间参数类型也不同)当前保持 expectedValue,则以原子方式将变量设置为 updateValue,并在成功时报告 true。此包中的类还包含获取并无条件设置值的方法,以及以下描述的较弱条件的原子更新操作 weakCompareAndSet

      原子访问和更新的内存效果一般遵循以下可变规则:

    • get 具有读取 volatile 变量的内存效果。
    • set 具有写入(分配)volatile 变量的内存效果。
    • 除了允许使用后续(但不是以前的)内存操作,其自身不施加带有普通的非 volatile 写入的重新排序约束,lazySet 具有写入(分配)volatile 变量的内存效果。在其他使用上下文中,当为 null 时(为了垃圾回收),lazySet 可以应用不会再次访问的引用。
    • weakCompareAndSet 以原子方式读取和有条件地写入变量但不创建任何 happen-before 排序,因此不提供与除 weakCompareAndSet 目标外任何变量以前或后续读取或写入操作有关的任何保证。
    • compareAndSet 和所有其他的读取和更新操作(如 getAndIncrement)都有读取和写入 volatile 变量的内存效果。

      设计原子类主要用作各种构造块,用于实现非阻塞数据结构和相关的基础结构类。compareAndSet 方法不是锁的常规替换方法。仅当对象的重要更新限定于单个 变量时才应用它。原子类不是 java.lang.Integer 和相关类的通用替换方法。它们 定义诸如 hashCodecompareTo 之类的方法。

      源码举例分析

      AtomicInteger继承结构:

      -java.lang.Object

      ---java.lang.Number

      ------java.util.concurrent.atomic.AtomicInteger

      还实现了java.io.Serializable以便序列化使用,然后是字段的定义和初始化:

      Unsafe里面定义了许多Native方法,通过JNI调用。value就是AtomicInteger代表的值,这里用volatile修饰用以保证value的可见性。

      //源码 setup to use Unsafe.compareAndSwapInt for updates
        private static final Unsafe unsafe = Unsafe.getUnsafe();
        private static final long valueOffset;
    
        static {
            try {
                valueOffset = unsafe.objectFieldOffset
                    (AtomicInteger.class.getDeclaredField("value"));
            } catch (Exception ex) { throw new Error(ex); }
        }
    
        private volatile int value;

      AtomicInteger大概实现了20个左右的public方法,下面举几个有代表性的来分析:

      1.get方法是用来获取当前最新的value。方法用final修饰是为了更进一步的保证线程安全。

        /**
         * Gets the current value.
         *
         * @return the current value
         */
        public final int get() {
            return value;
        }

      2.set方法是把value更新为一个新值。也是用final修饰。

        /**
         * Sets to the given value.
         *
         * @param newValue the new value
         */
        public final void set(int newValue) {
            value = newValue;
        }

      3.lazySet方法和set方法不同之处在于可以延时设置,就是在最后才设定新值。

        /**
         * Eventually sets to the given value.
         *
         * @param newValue the new value
         * @since 1.6
         */
        public final void lazySet(int newValue) {
            unsafe.putOrderedInt(this, valueOffset, newValue);
        }

      4.getAndSet方法是把新值设置为当前值然后返回旧值。

        /**
         * Atomically sets to the given value and returns the old value.
         *
         * @param newValue the new value
         * @return the previous value
         */
        public final int getAndSet(int newValue) {
            return unsafe.getAndSetInt(this, valueOffset, newValue);
        }

      5.compareAndSet方法是首先比较当前值与期望值,相同才更新到新值。如果更新成功则返回true,否则返回false。

        /**
         * Atomically sets the value to the given updated value
         * if the current value {@code ==} the expected value.
         *
         * @param expect the expected value
         * @param update the new value
         * @return {@code true} if successful. False return indicates that
         * the actual value was not equal to the expected value.
         */
        public final boolean compareAndSet(int expect, int update) {
            return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
        }

      6.getAndIncrement方法是原子的把原值加一并且返回旧值。getAndDecrement类似。

        /**
         * Atomically increments by one the current value.
         *
         * @return the previous value
         */
        public final int getAndIncrement() {
            return unsafe.getAndAddInt(this, valueOffset, 1);
        }

      7.getAndAdd方法是把原值加上某个值,并返回以前的值。

        /**
         * Atomically adds the given value to the current value.
         *
         * @param delta the value to add
         * @return the previous value
         */
        public final int getAndAdd(int delta) {
            return unsafe.getAndAddInt(this, valueOffset, delta);
        }

      8.incrementAndGet方法是把原值加一后返回新值。decrementAndGet类似。

        /**
         * Atomically increments by one the current value.
         *
         * @return the updated value
         */
        public final int incrementAndGet() {
            return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
        }

      9.getAndUpdate方法是根据一个定义的操作符来把当前值当第一个参数,传入的参数当做第二个参数更新value,返回旧值。updateAndGet是返回新值。 使用自旋的方式,调用了compareAndSet方法,只有返回了true才结束。

        /**
         * Atomically updates the current value with the results of
         * applying the given function, returning the previous value. The
         * function should be side-effect-free, since it may be re-applied
         * when attempted updates fail due to contention among threads.
         *
         * @param updateFunction a side-effect-free function
         * @return the previous value
         * @since 1.8
         */
        public final int getAndUpdate(IntUnaryOperator updateFunction) {
            int prev, next;
            do {
                prev = get();
                next = updateFunction.applyAsInt(prev);
            } while (!compareAndSet(prev, next));
            return prev;
        }

      以上这些方法都是原子操作。

      

      

      

      

  • 相关阅读:
    Autofs
    markdown 基本语法
    Why Linux Doesn’t Need Defragmenting
    How to reconfigure installed dpkg package (tzdata, locales)
    weblogic性能监控
    exec
    在Oracle中查询表的大小
    hadoop主要概念的理解和学习
    ORACLE ASM中查询表空间使用情况、数据文件路径、裸设备磁盘总大小剩余大小
    linux 查看内存和cpu占用比较多的进程
  • 原文地址:https://www.cnblogs.com/wxisme/p/6341273.html
Copyright © 2011-2022 走看看