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