46 * @author Doug Lea |
46 * @author Doug Lea |
47 */ |
47 */ |
48 public class AtomicIntegerArray implements java.io.Serializable { |
48 public class AtomicIntegerArray implements java.io.Serializable { |
49 private static final long serialVersionUID = 2862133569453604235L; |
49 private static final long serialVersionUID = 2862133569453604235L; |
50 |
50 |
51 // setup to use Unsafe.compareAndSwapInt for updates |
|
52 private static final Unsafe unsafe = Unsafe.getUnsafe(); |
51 private static final Unsafe unsafe = Unsafe.getUnsafe(); |
53 private static final int base = unsafe.arrayBaseOffset(int[].class); |
52 private static final int base = unsafe.arrayBaseOffset(int[].class); |
54 private static final int scale = unsafe.arrayIndexScale(int[].class); |
53 private static final int shift; |
55 private final int[] array; |
54 private final int[] array; |
56 |
55 |
57 private long rawIndex(int i) { |
56 static { |
|
57 int scale = unsafe.arrayIndexScale(int[].class); |
|
58 if ((scale & (scale - 1)) != 0) |
|
59 throw new Error("data type scale not a power of two"); |
|
60 shift = 31 - Integer.numberOfLeadingZeros(scale); |
|
61 } |
|
62 |
|
63 private long checkedByteOffset(int i) { |
58 if (i < 0 || i >= array.length) |
64 if (i < 0 || i >= array.length) |
59 throw new IndexOutOfBoundsException("index " + i); |
65 throw new IndexOutOfBoundsException("index " + i); |
60 return base + (long) i * scale; |
66 |
61 } |
67 return byteOffset(i); |
62 |
68 } |
63 /** |
69 |
64 * Creates a new AtomicIntegerArray of given length. |
70 private static long byteOffset(int i) { |
|
71 return ((long) i << shift) + base; |
|
72 } |
|
73 |
|
74 /** |
|
75 * Creates a new AtomicIntegerArray of the given length, with all |
|
76 * elements initially zero. |
65 * |
77 * |
66 * @param length the length of the array |
78 * @param length the length of the array |
67 */ |
79 */ |
68 public AtomicIntegerArray(int length) { |
80 public AtomicIntegerArray(int length) { |
69 array = new int[length]; |
81 array = new int[length]; |
70 // must perform at least one volatile write to conform to JMM |
|
71 if (length > 0) |
|
72 unsafe.putIntVolatile(array, rawIndex(0), 0); |
|
73 } |
82 } |
74 |
83 |
75 /** |
84 /** |
76 * Creates a new AtomicIntegerArray with the same length as, and |
85 * Creates a new AtomicIntegerArray with the same length as, and |
77 * all elements copied from, the given array. |
86 * all elements copied from, the given array. |
78 * |
87 * |
79 * @param array the array to copy elements from |
88 * @param array the array to copy elements from |
80 * @throws NullPointerException if array is null |
89 * @throws NullPointerException if array is null |
81 */ |
90 */ |
82 public AtomicIntegerArray(int[] array) { |
91 public AtomicIntegerArray(int[] array) { |
83 if (array == null) |
92 // Visibility guaranteed by final field guarantees |
84 throw new NullPointerException(); |
93 this.array = array.clone(); |
85 int length = array.length; |
|
86 this.array = new int[length]; |
|
87 if (length > 0) { |
|
88 int last = length-1; |
|
89 for (int i = 0; i < last; ++i) |
|
90 this.array[i] = array[i]; |
|
91 // Do the last write as volatile |
|
92 unsafe.putIntVolatile(this.array, rawIndex(last), array[last]); |
|
93 } |
|
94 } |
94 } |
95 |
95 |
96 /** |
96 /** |
97 * Returns the length of the array. |
97 * Returns the length of the array. |
98 * |
98 * |
107 * |
107 * |
108 * @param i the index |
108 * @param i the index |
109 * @return the current value |
109 * @return the current value |
110 */ |
110 */ |
111 public final int get(int i) { |
111 public final int get(int i) { |
112 return unsafe.getIntVolatile(array, rawIndex(i)); |
112 return getRaw(checkedByteOffset(i)); |
|
113 } |
|
114 |
|
115 private int getRaw(long offset) { |
|
116 return unsafe.getIntVolatile(array, offset); |
113 } |
117 } |
114 |
118 |
115 /** |
119 /** |
116 * Sets the element at position {@code i} to the given value. |
120 * Sets the element at position {@code i} to the given value. |
117 * |
121 * |
118 * @param i the index |
122 * @param i the index |
119 * @param newValue the new value |
123 * @param newValue the new value |
120 */ |
124 */ |
121 public final void set(int i, int newValue) { |
125 public final void set(int i, int newValue) { |
122 unsafe.putIntVolatile(array, rawIndex(i), newValue); |
126 unsafe.putIntVolatile(array, checkedByteOffset(i), newValue); |
123 } |
127 } |
124 |
128 |
125 /** |
129 /** |
126 * Eventually sets the element at position {@code i} to the given value. |
130 * Eventually sets the element at position {@code i} to the given value. |
127 * |
131 * |
128 * @param i the index |
132 * @param i the index |
129 * @param newValue the new value |
133 * @param newValue the new value |
130 * @since 1.6 |
134 * @since 1.6 |
131 */ |
135 */ |
132 public final void lazySet(int i, int newValue) { |
136 public final void lazySet(int i, int newValue) { |
133 unsafe.putOrderedInt(array, rawIndex(i), newValue); |
137 unsafe.putOrderedInt(array, checkedByteOffset(i), newValue); |
134 } |
138 } |
135 |
139 |
136 /** |
140 /** |
137 * Atomically sets the element at position {@code i} to the given |
141 * Atomically sets the element at position {@code i} to the given |
138 * value and returns the old value. |
142 * value and returns the old value. |
158 * @param update the new value |
163 * @param update the new value |
159 * @return true if successful. False return indicates that |
164 * @return true if successful. False return indicates that |
160 * the actual value was not equal to the expected value. |
165 * the actual value was not equal to the expected value. |
161 */ |
166 */ |
162 public final boolean compareAndSet(int i, int expect, int update) { |
167 public final boolean compareAndSet(int i, int expect, int update) { |
163 return unsafe.compareAndSwapInt(array, rawIndex(i), |
168 return compareAndSetRaw(checkedByteOffset(i), expect, update); |
164 expect, update); |
169 } |
|
170 |
|
171 private boolean compareAndSetRaw(long offset, int expect, int update) { |
|
172 return unsafe.compareAndSwapInt(array, offset, expect, update); |
165 } |
173 } |
166 |
174 |
167 /** |
175 /** |
168 * Atomically sets the element at position {@code i} to the given |
176 * Atomically sets the element at position {@code i} to the given |
169 * updated value if the current value {@code ==} the expected value. |
177 * updated value if the current value {@code ==} the expected value. |
186 * |
194 * |
187 * @param i the index |
195 * @param i the index |
188 * @return the previous value |
196 * @return the previous value |
189 */ |
197 */ |
190 public final int getAndIncrement(int i) { |
198 public final int getAndIncrement(int i) { |
|
199 return getAndAdd(i, 1); |
|
200 } |
|
201 |
|
202 /** |
|
203 * Atomically decrements by one the element at index {@code i}. |
|
204 * |
|
205 * @param i the index |
|
206 * @return the previous value |
|
207 */ |
|
208 public final int getAndDecrement(int i) { |
|
209 return getAndAdd(i, -1); |
|
210 } |
|
211 |
|
212 /** |
|
213 * Atomically adds the given value to the element at index {@code i}. |
|
214 * |
|
215 * @param i the index |
|
216 * @param delta the value to add |
|
217 * @return the previous value |
|
218 */ |
|
219 public final int getAndAdd(int i, int delta) { |
|
220 long offset = checkedByteOffset(i); |
191 while (true) { |
221 while (true) { |
192 int current = get(i); |
222 int current = getRaw(offset); |
193 int next = current + 1; |
223 if (compareAndSetRaw(offset, current, current + delta)) |
194 if (compareAndSet(i, current, next)) |
|
195 return current; |
224 return current; |
196 } |
225 } |
197 } |
226 } |
198 |
227 |
199 /** |
228 /** |
|
229 * Atomically increments by one the element at index {@code i}. |
|
230 * |
|
231 * @param i the index |
|
232 * @return the updated value |
|
233 */ |
|
234 public final int incrementAndGet(int i) { |
|
235 return addAndGet(i, 1); |
|
236 } |
|
237 |
|
238 /** |
200 * Atomically decrements by one the element at index {@code i}. |
239 * Atomically decrements by one the element at index {@code i}. |
201 * |
240 * |
202 * @param i the index |
241 * @param i the index |
203 * @return the previous value |
|
204 */ |
|
205 public final int getAndDecrement(int i) { |
|
206 while (true) { |
|
207 int current = get(i); |
|
208 int next = current - 1; |
|
209 if (compareAndSet(i, current, next)) |
|
210 return current; |
|
211 } |
|
212 } |
|
213 |
|
214 /** |
|
215 * Atomically adds the given value to the element at index {@code i}. |
|
216 * |
|
217 * @param i the index |
|
218 * @param delta the value to add |
|
219 * @return the previous value |
|
220 */ |
|
221 public final int getAndAdd(int i, int delta) { |
|
222 while (true) { |
|
223 int current = get(i); |
|
224 int next = current + delta; |
|
225 if (compareAndSet(i, current, next)) |
|
226 return current; |
|
227 } |
|
228 } |
|
229 |
|
230 /** |
|
231 * Atomically increments by one the element at index {@code i}. |
|
232 * |
|
233 * @param i the index |
|
234 * @return the updated value |
242 * @return the updated value |
235 */ |
243 */ |
236 public final int incrementAndGet(int i) { |
|
237 while (true) { |
|
238 int current = get(i); |
|
239 int next = current + 1; |
|
240 if (compareAndSet(i, current, next)) |
|
241 return next; |
|
242 } |
|
243 } |
|
244 |
|
245 /** |
|
246 * Atomically decrements by one the element at index {@code i}. |
|
247 * |
|
248 * @param i the index |
|
249 * @return the updated value |
|
250 */ |
|
251 public final int decrementAndGet(int i) { |
244 public final int decrementAndGet(int i) { |
252 while (true) { |
245 return addAndGet(i, -1); |
253 int current = get(i); |
|
254 int next = current - 1; |
|
255 if (compareAndSet(i, current, next)) |
|
256 return next; |
|
257 } |
|
258 } |
246 } |
259 |
247 |
260 /** |
248 /** |
261 * Atomically adds the given value to the element at index {@code i}. |
249 * Atomically adds the given value to the element at index {@code i}. |
262 * |
250 * |
263 * @param i the index |
251 * @param i the index |
264 * @param delta the value to add |
252 * @param delta the value to add |
265 * @return the updated value |
253 * @return the updated value |
266 */ |
254 */ |
267 public final int addAndGet(int i, int delta) { |
255 public final int addAndGet(int i, int delta) { |
|
256 long offset = checkedByteOffset(i); |
268 while (true) { |
257 while (true) { |
269 int current = get(i); |
258 int current = getRaw(offset); |
270 int next = current + delta; |
259 int next = current + delta; |
271 if (compareAndSet(i, current, next)) |
260 if (compareAndSetRaw(offset, current, next)) |
272 return next; |
261 return next; |
273 } |
262 } |
274 } |
263 } |
275 |
264 |
276 /** |
265 /** |
277 * Returns the String representation of the current values of array. |
266 * Returns the String representation of the current values of array. |
278 * @return the String representation of the current values of array. |
267 * @return the String representation of the current values of array |
279 */ |
268 */ |
280 public String toString() { |
269 public String toString() { |
281 if (array.length > 0) // force volatile read |
270 int iMax = array.length - 1; |
282 get(0); |
271 if (iMax == -1) |
283 return Arrays.toString(array); |
272 return "[]"; |
|
273 |
|
274 StringBuilder b = new StringBuilder(); |
|
275 b.append('['); |
|
276 for (int i = 0; ; i++) { |
|
277 b.append(getRaw(byteOffset(i))); |
|
278 if (i == iMax) |
|
279 return b.append(']').toString(); |
|
280 b.append(',').append(' '); |
|
281 } |
284 } |
282 } |
285 |
283 |
286 } |
284 } |