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 9 import java.util.function.IntBinaryOperator; 10 import java.util.function.IntUnaryOperator; 11 12 /** 13 * An {@code int} array in which elements may be updated atomically. 14 * See the {@link java.util.concurrent.atomic} package 15 * specification for description of the properties of atomic 16 * variables. 17 * @since 1.5 18 * @author Doug Lea 19 */ 20 public class AtomicIntegerArray implements java.io.Serializable { 21 private static final long serialVersionUID = 2862133569453604235L; 22 23 private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); 24 private static final int ABASE; 25 private static final int ASHIFT; 26 private final int[] array; 27 28 static { 29 ABASE = U.arrayBaseOffset(int[].class); 30 int scale = U.arrayIndexScale(int[].class); 31 if ((scale & (scale - 1)) != 0) 32 throw new Error("array index scale not a power of two"); 33 ASHIFT = 31 - Integer.numberOfLeadingZeros(scale); 34 } 35 checkedByteOffset(int i)36 private long checkedByteOffset(int i) { 37 if (i < 0 || i >= array.length) 38 throw new IndexOutOfBoundsException("index " + i); 39 40 return byteOffset(i); 41 } 42 byteOffset(int i)43 private static long byteOffset(int i) { 44 return ((long) i << ASHIFT) + ABASE; 45 } 46 47 /** 48 * Creates a new AtomicIntegerArray of the given length, with all 49 * elements initially zero. 50 * 51 * @param length the length of the array 52 */ AtomicIntegerArray(int length)53 public AtomicIntegerArray(int length) { 54 array = new int[length]; 55 } 56 57 /** 58 * Creates a new AtomicIntegerArray with the same length as, and 59 * all elements copied from, the given array. 60 * 61 * @param array the array to copy elements from 62 * @throws NullPointerException if array is null 63 */ AtomicIntegerArray(int[] array)64 public AtomicIntegerArray(int[] array) { 65 // Visibility guaranteed by final field guarantees 66 this.array = array.clone(); 67 } 68 69 /** 70 * Returns the length of the array. 71 * 72 * @return the length of the array 73 */ length()74 public final int length() { 75 return array.length; 76 } 77 78 /** 79 * Gets the current value at position {@code i}. 80 * 81 * @param i the index 82 * @return the current value 83 */ get(int i)84 public final int get(int i) { 85 return getRaw(checkedByteOffset(i)); 86 } 87 getRaw(long offset)88 private int getRaw(long offset) { 89 return U.getIntVolatile(array, offset); 90 } 91 92 /** 93 * Sets the element at position {@code i} to the given value. 94 * 95 * @param i the index 96 * @param newValue the new value 97 */ set(int i, int newValue)98 public final void set(int i, int newValue) { 99 U.putIntVolatile(array, checkedByteOffset(i), newValue); 100 } 101 102 /** 103 * Eventually sets the element at position {@code i} to the given value. 104 * 105 * @param i the index 106 * @param newValue the new value 107 * @since 1.6 108 */ lazySet(int i, int newValue)109 public final void lazySet(int i, int newValue) { 110 U.putOrderedInt(array, checkedByteOffset(i), newValue); 111 } 112 113 /** 114 * Atomically sets the element at position {@code i} to the given 115 * value and returns the old value. 116 * 117 * @param i the index 118 * @param newValue the new value 119 * @return the previous value 120 */ getAndSet(int i, int newValue)121 public final int getAndSet(int i, int newValue) { 122 return U.getAndSetInt(array, checkedByteOffset(i), newValue); 123 } 124 125 /** 126 * Atomically sets the element at position {@code i} to the given 127 * updated value if the current value {@code ==} the expected value. 128 * 129 * @param i the index 130 * @param expect the expected value 131 * @param update the new value 132 * @return {@code true} if successful. False return indicates that 133 * the actual value was not equal to the expected value. 134 */ compareAndSet(int i, int expect, int update)135 public final boolean compareAndSet(int i, int expect, int update) { 136 return compareAndSetRaw(checkedByteOffset(i), expect, update); 137 } 138 compareAndSetRaw(long offset, int expect, int update)139 private boolean compareAndSetRaw(long offset, int expect, int update) { 140 return U.compareAndSwapInt(array, offset, expect, update); 141 } 142 143 /** 144 * Atomically sets the element at position {@code i} to the given 145 * updated value if the current value {@code ==} the expected value. 146 * 147 * <p><a href="package-summary.html#weakCompareAndSet">May fail 148 * spuriously and does not provide ordering guarantees</a>, so is 149 * only rarely an appropriate alternative to {@code compareAndSet}. 150 * 151 * @param i the index 152 * @param expect the expected value 153 * @param update the new value 154 * @return {@code true} if successful 155 */ weakCompareAndSet(int i, int expect, int update)156 public final boolean weakCompareAndSet(int i, int expect, int update) { 157 return compareAndSet(i, expect, update); 158 } 159 160 /** 161 * Atomically increments by one the element at index {@code i}. 162 * 163 * @param i the index 164 * @return the previous value 165 */ getAndIncrement(int i)166 public final int getAndIncrement(int i) { 167 return getAndAdd(i, 1); 168 } 169 170 /** 171 * Atomically decrements by one the element at index {@code i}. 172 * 173 * @param i the index 174 * @return the previous value 175 */ getAndDecrement(int i)176 public final int getAndDecrement(int i) { 177 return getAndAdd(i, -1); 178 } 179 180 /** 181 * Atomically adds the given value to the element at index {@code i}. 182 * 183 * @param i the index 184 * @param delta the value to add 185 * @return the previous value 186 */ getAndAdd(int i, int delta)187 public final int getAndAdd(int i, int delta) { 188 return U.getAndAddInt(array, checkedByteOffset(i), delta); 189 } 190 191 /** 192 * Atomically increments by one the element at index {@code i}. 193 * 194 * @param i the index 195 * @return the updated value 196 */ incrementAndGet(int i)197 public final int incrementAndGet(int i) { 198 return getAndAdd(i, 1) + 1; 199 } 200 201 /** 202 * Atomically decrements by one the element at index {@code i}. 203 * 204 * @param i the index 205 * @return the updated value 206 */ decrementAndGet(int i)207 public final int decrementAndGet(int i) { 208 return getAndAdd(i, -1) - 1; 209 } 210 211 /** 212 * Atomically adds the given value to the element at index {@code i}. 213 * 214 * @param i the index 215 * @param delta the value to add 216 * @return the updated value 217 */ addAndGet(int i, int delta)218 public final int addAndGet(int i, int delta) { 219 return getAndAdd(i, delta) + delta; 220 } 221 222 /** 223 * Atomically updates the element at index {@code i} with the results 224 * of applying the given function, returning the previous value. The 225 * function should be side-effect-free, since it may be re-applied 226 * when attempted updates fail due to contention among threads. 227 * 228 * @param i the index 229 * @param updateFunction a side-effect-free function 230 * @return the previous value 231 * @since 1.8 232 */ getAndUpdate(int i, IntUnaryOperator updateFunction)233 public final int getAndUpdate(int i, IntUnaryOperator updateFunction) { 234 long offset = checkedByteOffset(i); 235 int prev, next; 236 do { 237 prev = getRaw(offset); 238 next = updateFunction.applyAsInt(prev); 239 } while (!compareAndSetRaw(offset, prev, next)); 240 return prev; 241 } 242 243 /** 244 * Atomically updates the element at index {@code i} with the results 245 * of applying the given function, returning the updated value. The 246 * function should be side-effect-free, since it may be re-applied 247 * when attempted updates fail due to contention among threads. 248 * 249 * @param i the index 250 * @param updateFunction a side-effect-free function 251 * @return the updated value 252 * @since 1.8 253 */ updateAndGet(int i, IntUnaryOperator updateFunction)254 public final int updateAndGet(int i, IntUnaryOperator updateFunction) { 255 long offset = checkedByteOffset(i); 256 int prev, next; 257 do { 258 prev = getRaw(offset); 259 next = updateFunction.applyAsInt(prev); 260 } while (!compareAndSetRaw(offset, prev, next)); 261 return next; 262 } 263 264 /** 265 * Atomically updates the element at index {@code i} with the 266 * results of applying the given function to the current and 267 * given values, returning the previous value. The function should 268 * be side-effect-free, since it may be re-applied when attempted 269 * updates fail due to contention among threads. The function is 270 * applied with the current value at index {@code i} as its first 271 * argument, and the given update as the second argument. 272 * 273 * @param i the index 274 * @param x the update value 275 * @param accumulatorFunction a side-effect-free function of two arguments 276 * @return the previous value 277 * @since 1.8 278 */ getAndAccumulate(int i, int x, IntBinaryOperator accumulatorFunction)279 public final int getAndAccumulate(int i, int x, 280 IntBinaryOperator accumulatorFunction) { 281 long offset = checkedByteOffset(i); 282 int prev, next; 283 do { 284 prev = getRaw(offset); 285 next = accumulatorFunction.applyAsInt(prev, x); 286 } while (!compareAndSetRaw(offset, prev, next)); 287 return prev; 288 } 289 290 /** 291 * Atomically updates the element at index {@code i} with the 292 * results of applying the given function to the current and 293 * given values, returning the updated value. The function should 294 * be side-effect-free, since it may be re-applied when attempted 295 * updates fail due to contention among threads. The function is 296 * applied with the current value at index {@code i} as its first 297 * argument, and the given update as the second argument. 298 * 299 * @param i the index 300 * @param x the update value 301 * @param accumulatorFunction a side-effect-free function of two arguments 302 * @return the updated value 303 * @since 1.8 304 */ accumulateAndGet(int i, int x, IntBinaryOperator accumulatorFunction)305 public final int accumulateAndGet(int i, int x, 306 IntBinaryOperator accumulatorFunction) { 307 long offset = checkedByteOffset(i); 308 int prev, next; 309 do { 310 prev = getRaw(offset); 311 next = accumulatorFunction.applyAsInt(prev, x); 312 } while (!compareAndSetRaw(offset, prev, next)); 313 return next; 314 } 315 316 /** 317 * Returns the String representation of the current values of array. 318 * @return the String representation of the current values of array 319 */ toString()320 public String toString() { 321 int iMax = array.length - 1; 322 if (iMax == -1) 323 return "[]"; 324 325 StringBuilder b = new StringBuilder(); 326 b.append('['); 327 for (int i = 0; ; i++) { 328 b.append(getRaw(byteOffset(i))); 329 if (i == iMax) 330 return b.append(']').toString(); 331 b.append(',').append(' '); 332 } 333 } 334 335 } 336