jdk/src/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java
changeset 7976 f273c0d04215
parent 5506 202f599c92aa
child 9242 ef138d47df58
equal deleted inserted replaced
7975:f0de2d05f34c 7976:f273c0d04215
    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.
   140      * @param i the index
   144      * @param i the index
   141      * @param newValue the new value
   145      * @param newValue the new value
   142      * @return the previous value
   146      * @return the previous value
   143      */
   147      */
   144     public final int getAndSet(int i, int newValue) {
   148     public final int getAndSet(int i, int newValue) {
       
   149         long offset = checkedByteOffset(i);
   145         while (true) {
   150         while (true) {
   146             int current = get(i);
   151             int current = getRaw(offset);
   147             if (compareAndSet(i, current, newValue))
   152             if (compareAndSetRaw(offset, current, newValue))
   148                 return current;
   153                 return current;
   149         }
   154         }
   150     }
   155     }
   151 
   156 
   152     /**
   157     /**
   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 }