Home | History | Annotate | Download | only in atomic
      1 /*
      2  * Written by Doug Lea with assistance from members of JCP JSR-166
      3  * Expert Group and released to the public domain, as explained at
      4  * http://creativecommons.org/publicdomain/zero/1.0/
      5  */
      6 
      7 package java.util.concurrent.atomic;
      8 import sun.misc.Unsafe;
      9 
     10 /**
     11  * An {@code int} array in which elements may be updated atomically.
     12  * See the {@link java.util.concurrent.atomic} package
     13  * specification for description of the properties of atomic
     14  * variables.
     15  * @since 1.5
     16  * @author Doug Lea
     17  */
     18 public class AtomicIntegerArray implements java.io.Serializable {
     19     private static final long serialVersionUID = 2862133569453604235L;
     20 
     21     private static final Unsafe unsafe = Unsafe.getUnsafe();
     22     private static final int base = unsafe.arrayBaseOffset(int[].class);
     23     private static final int shift;
     24     private final int[] array;
     25 
     26     static {
     27         int scale = unsafe.arrayIndexScale(int[].class);
     28         if ((scale & (scale - 1)) != 0)
     29             throw new Error("data type scale not a power of two");
     30         shift = 31 - Integer.numberOfLeadingZeros(scale);
     31     }
     32 
     33     private long checkedByteOffset(int i) {
     34         if (i < 0 || i >= array.length)
     35             throw new IndexOutOfBoundsException("index " + i);
     36 
     37         return byteOffset(i);
     38     }
     39 
     40     private static long byteOffset(int i) {
     41         return ((long) i << shift) + base;
     42     }
     43 
     44     /**
     45      * Creates a new AtomicIntegerArray of the given length, with all
     46      * elements initially zero.
     47      *
     48      * @param length the length of the array
     49      */
     50     public AtomicIntegerArray(int length) {
     51         array = new int[length];
     52     }
     53 
     54     /**
     55      * Creates a new AtomicIntegerArray with the same length as, and
     56      * all elements copied from, the given array.
     57      *
     58      * @param array the array to copy elements from
     59      * @throws NullPointerException if array is null
     60      */
     61     public AtomicIntegerArray(int[] array) {
     62         // Visibility guaranteed by final field guarantees
     63         this.array = array.clone();
     64     }
     65 
     66     /**
     67      * Returns the length of the array.
     68      *
     69      * @return the length of the array
     70      */
     71     public final int length() {
     72         return array.length;
     73     }
     74 
     75     /**
     76      * Gets the current value at position {@code i}.
     77      *
     78      * @param i the index
     79      * @return the current value
     80      */
     81     public final int get(int i) {
     82         return getRaw(checkedByteOffset(i));
     83     }
     84 
     85     private int getRaw(long offset) {
     86         return unsafe.getIntVolatile(array, offset);
     87     }
     88 
     89     /**
     90      * Sets the element at position {@code i} to the given value.
     91      *
     92      * @param i the index
     93      * @param newValue the new value
     94      */
     95     public final void set(int i, int newValue) {
     96         unsafe.putIntVolatile(array, checkedByteOffset(i), newValue);
     97     }
     98 
     99     /**
    100      * Eventually sets the element at position {@code i} to the given value.
    101      *
    102      * @param i the index
    103      * @param newValue the new value
    104      * @since 1.6
    105      */
    106     public final void lazySet(int i, int newValue) {
    107         unsafe.putOrderedInt(array, checkedByteOffset(i), newValue);
    108     }
    109 
    110     /**
    111      * Atomically sets the element at position {@code i} to the given
    112      * value and returns the old value.
    113      *
    114      * @param i the index
    115      * @param newValue the new value
    116      * @return the previous value
    117      */
    118     public final int getAndSet(int i, int newValue) {
    119         long offset = checkedByteOffset(i);
    120         while (true) {
    121             int current = getRaw(offset);
    122             if (compareAndSetRaw(offset, current, newValue))
    123                 return current;
    124         }
    125     }
    126 
    127     /**
    128      * Atomically sets the element at position {@code i} to the given
    129      * updated value if the current value {@code ==} the expected value.
    130      *
    131      * @param i the index
    132      * @param expect the expected value
    133      * @param update the new value
    134      * @return true if successful. False return indicates that
    135      * the actual value was not equal to the expected value.
    136      */
    137     public final boolean compareAndSet(int i, int expect, int update) {
    138         return compareAndSetRaw(checkedByteOffset(i), expect, update);
    139     }
    140 
    141     private boolean compareAndSetRaw(long offset, int expect, int update) {
    142         return unsafe.compareAndSwapInt(array, offset, expect, update);
    143     }
    144 
    145     /**
    146      * Atomically sets the element at position {@code i} to the given
    147      * updated value if the current value {@code ==} the expected value.
    148      *
    149      * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
    150      * and does not provide ordering guarantees, so is only rarely an
    151      * appropriate alternative to {@code compareAndSet}.
    152      *
    153      * @param i the index
    154      * @param expect the expected value
    155      * @param update the new value
    156      * @return true if successful.
    157      */
    158     public final boolean weakCompareAndSet(int i, int expect, int update) {
    159         return compareAndSet(i, expect, update);
    160     }
    161 
    162     /**
    163      * Atomically increments by one the element at index {@code i}.
    164      *
    165      * @param i the index
    166      * @return the previous value
    167      */
    168     public final int getAndIncrement(int i) {
    169         return getAndAdd(i, 1);
    170     }
    171 
    172     /**
    173      * Atomically decrements by one the element at index {@code i}.
    174      *
    175      * @param i the index
    176      * @return the previous value
    177      */
    178     public final int getAndDecrement(int i) {
    179         return getAndAdd(i, -1);
    180     }
    181 
    182     /**
    183      * Atomically adds the given value to the element at index {@code i}.
    184      *
    185      * @param i the index
    186      * @param delta the value to add
    187      * @return the previous value
    188      */
    189     public final int getAndAdd(int i, int delta) {
    190         long offset = checkedByteOffset(i);
    191         while (true) {
    192             int current = getRaw(offset);
    193             if (compareAndSetRaw(offset, current, current + delta))
    194                 return current;
    195         }
    196     }
    197 
    198     /**
    199      * Atomically increments by one the element at index {@code i}.
    200      *
    201      * @param i the index
    202      * @return the updated value
    203      */
    204     public final int incrementAndGet(int i) {
    205         return addAndGet(i, 1);
    206     }
    207 
    208     /**
    209      * Atomically decrements by one the element at index {@code i}.
    210      *
    211      * @param i the index
    212      * @return the updated value
    213      */
    214     public final int decrementAndGet(int i) {
    215         return addAndGet(i, -1);
    216     }
    217 
    218     /**
    219      * Atomically adds the given value to the element at index {@code i}.
    220      *
    221      * @param i the index
    222      * @param delta the value to add
    223      * @return the updated value
    224      */
    225     public final int addAndGet(int i, int delta) {
    226         long offset = checkedByteOffset(i);
    227         while (true) {
    228             int current = getRaw(offset);
    229             int next = current + delta;
    230             if (compareAndSetRaw(offset, current, next))
    231                 return next;
    232         }
    233     }
    234 
    235     /**
    236      * Returns the String representation of the current values of array.
    237      * @return the String representation of the current values of array
    238      */
    239     public String toString() {
    240         int iMax = array.length - 1;
    241         if (iMax == -1)
    242             return "[]";
    243 
    244         StringBuilder b = new StringBuilder();
    245         b.append('[');
    246         for (int i = 0; ; i++) {
    247             b.append(getRaw(byteOffset(i)));
    248             if (i == iMax)
    249                 return b.append(']').toString();
    250             b.append(',').append(' ');
    251         }
    252     }
    253 
    254 }
    255