Java Multithread Series - [JUC Atomic Class 02] - Atomic Long Array Atomic Class

Keywords: Java JDK Oracle

Reference: http://www.cnblogs.com/skywang12345/p/java_threads_category.html

outline

Atomic Integer Array, Atomic Long Array and Atomic Reference Array are three types of arrays with similar principles and usages. This chapter introduces atomic classes of array type with Atomic Long Array. The contents include:
Introduction to Atomic Long Array and List of Functions
Atomic Long Array source code analysis (based on JDK 1.7.0_40)
AtomicLong Array example

For reprinting, please indicate the source: http://www.cnblogs.com/skywang12345/p/3514604.html

 

Introduction to Atomic Long Array and List of Functions

In " Java Multithread Series--AtomicLong Atomic Class of "JUC Atomic Class" 02 "As mentioned, AtomicLong operates atomically on long shaping. Atomic Long Array operates atomically on long integer arrays.

 

AtomicLong Array Function List

// Create a new Atomic Long Array of a given length.
AtomicLongArray(int length)
// Create a new AtomicLongArray of the same length as a given array and copy all its elements from the given array.
AtomicLongArray(long[] array)

// Atomically adds a given value to the element of index i.
long addAndGet(int i, long delta)
// If the current value == the expected value, the value is set atomically to the given updated value.
boolean compareAndSet(int i, long expect, long update)
// Atomically subtract the element of index i by 1.
long decrementAndGet(int i)
// Gets the current value of location i.
long get(int i)
// Atomically adds the given value to the element of index i.
long getAndAdd(int i, long delta)
// Atomically subtract the element of index i by 1.
long getAndDecrement(int i)
// Atomically add the element of index i to 1.
long getAndIncrement(int i)
// Atomically sets the element of position i to a given value and returns the old value.
long getAndSet(int i, long newValue)
// Atomically add the element of index i to 1.
long incrementAndGet(int i)
// Eventually sets the element of position i to a given value.
void lazySet(int i, long newValue)
// Returns the length of the array.
int length()
// Sets the element of position i to a given value.
void set(int i, long newValue)
// Returns the string representation of the current value of the array.
String toString()
// If the current value == the expected value, the value is set atomically to the given updated value.
boolean    weakCompareAndSet(int i, long expect, long update)

 

Atomic Long Array source code analysis (based on JDK 1.7.0_40)

Full source code for AtomicLong Array

/*
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 */

/*
 * Written by Doug Lea with assistance from members of JCP JSR-166
 * Expert Group and released to the public domain, as explained at
 * http://creativecommons.org/publicdomain/zero/1.0/
 */

package java.util.concurrent.atomic;
import sun.misc.Unsafe;
import java.util.*;

/**
 * A {@code long} array in which elements may be updated atomically.
 * See the {@link java.util.concurrent.atomic} package specification
 * for description of the properties of atomic variables.
 * @since 1.5
 * @author Doug Lea
 */
public class AtomicLongArray implements java.io.Serializable {
    private static final long serialVersionUID = -2308431214976778248L;

    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final int base = unsafe.arrayBaseOffset(long[].class);
    private static final int shift;
    private final long[] array;

    static {
        int scale = unsafe.arrayIndexScale(long[].class);
        if ((scale & (scale - 1)) != 0)
            throw new Error("data type scale not a power of two");
        shift = 31 - Integer.numberOfLeadingZeros(scale);
    }

    private long checkedByteOffset(int i) {
        if (i < 0 || i >= array.length)
            throw new IndexOutOfBoundsException("index " + i);

        return byteOffset(i);
    }

    private static long byteOffset(int i) {
        return ((long) i << shift) + base;
    }

    /**
     * Creates a new AtomicLongArray of the given length, with all
     * elements initially zero.
     *
     * @param length the length of the array
     */
    public AtomicLongArray(int length) {
        array = new long[length];
    }

    /**
     * Creates a new AtomicLongArray with the same length as, and
     * all elements copied from, the given array.
     *
     * @param array the array to copy elements from
     * @throws NullPointerException if array is null
     */
    public AtomicLongArray(long[] array) {
        // Visibility guaranteed by final field guarantees
        this.array = array.clone();
    }

    /**
     * Returns the length of the array.
     *
     * @return the length of the array
     */
    public final int length() {
        return array.length;
    }

    /**
     * Gets the current value at position {@code i}.
     *
     * @param i the index
     * @return the current value
     */
    public final long get(int i) {
        return getRaw(checkedByteOffset(i));
    }

    private long getRaw(long offset) {
        return unsafe.getLongVolatile(array, offset);
    }

    /**
     * Sets the element at position {@code i} to the given value.
     *
     * @param i the index
     * @param newValue the new value
     */
    public final void set(int i, long newValue) {
        unsafe.putLongVolatile(array, checkedByteOffset(i), newValue);
    }

    /**
     * Eventually sets the element at position {@code i} to the given value.
     *
     * @param i the index
     * @param newValue the new value
     * @since 1.6
     */
    public final void lazySet(int i, long newValue) {
        unsafe.putOrderedLong(array, checkedByteOffset(i), newValue);
    }


    /**
     * Atomically sets the element at position {@code i} to the given value
     * and returns the old value.
     *
     * @param i the index
     * @param newValue the new value
     * @return the previous value
     */
    public final long getAndSet(int i, long newValue) {
        long offset = checkedByteOffset(i);
        while (true) {
            long current = getRaw(offset);
            if (compareAndSetRaw(offset, current, newValue))
                return current;
        }
    }

    /**
     * Atomically sets the element at position {@code i} to the given
     * updated value if the current value {@code ==} the expected value.
     *
     * @param i the index
     * @param expect the expected value
     * @param update the new value
     * @return true if successful. False return indicates that
     * the actual value was not equal to the expected value.
     */
    public final boolean compareAndSet(int i, long expect, long update) {
        return compareAndSetRaw(checkedByteOffset(i), expect, update);
    }

    private boolean compareAndSetRaw(long offset, long expect, long update) {
        return unsafe.compareAndSwapLong(array, offset, expect, update);
    }

    /**
     * Atomically sets the element at position {@code i} to the given
     * updated value if the current value {@code ==} the expected value.
     *
     * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
     * and does not provide ordering guarantees, so is only rarely an
     * appropriate alternative to {@code compareAndSet}.
     *
     * @param i the index
     * @param expect the expected value
     * @param update the new value
     * @return true if successful.
     */
    public final boolean weakCompareAndSet(int i, long expect, long update) {
        return compareAndSet(i, expect, update);
    }

    /**
     * Atomically increments by one the element at index {@code i}.
     *
     * @param i the index
     * @return the previous value
     */
    public final long getAndIncrement(int i) {
        return getAndAdd(i, 1);
    }

    /**
     * Atomically decrements by one the element at index {@code i}.
     *
     * @param i the index
     * @return the previous value
     */
    public final long getAndDecrement(int i) {
        return getAndAdd(i, -1);
    }

    /**
     * Atomically adds the given value to the element at index {@code i}.
     *
     * @param i the index
     * @param delta the value to add
     * @return the previous value
     */
    public final long getAndAdd(int i, long delta) {
        long offset = checkedByteOffset(i);
        while (true) {
            long current = getRaw(offset);
            if (compareAndSetRaw(offset, current, current + delta))
                return current;
        }
    }

    /**
     * Atomically increments by one the element at index {@code i}.
     *
     * @param i the index
     * @return the updated value
     */
    public final long incrementAndGet(int i) {
        return addAndGet(i, 1);
    }

    /**
     * Atomically decrements by one the element at index {@code i}.
     *
     * @param i the index
     * @return the updated value
     */
    public final long decrementAndGet(int i) {
        return addAndGet(i, -1);
    }

    /**
     * Atomically adds the given value to the element at index {@code i}.
     *
     * @param i the index
     * @param delta the value to add
     * @return the updated value
     */
    public long addAndGet(int i, long delta) {
        long offset = checkedByteOffset(i);
        while (true) {
            long current = getRaw(offset);
            long next = current + delta;
            if (compareAndSetRaw(offset, current, next))
                return next;
        }
    }

    /**
     * Returns the String representation of the current values of array.
     * @return the String representation of the current values of array
     */
    public String toString() {
        int iMax = array.length - 1;
        if (iMax == -1)
            return "[]";

        StringBuilder b = new StringBuilder();
        b.append('[');
        for (int i = 0; ; i++) {
            b.append(getRaw(byteOffset(i)));
            if (i == iMax)
                return b.append(']').toString();
            b.append(',').append(' ');
        }
    }

}

The code of AtomicLong Array is very simple. Let's just take incrementAndGet() as an example to illustrate the principle of AtomicLong.
The source code of incrementAndGet() is as follows:

public final long incrementAndGet(int i) {
    return addAndGet(i, 1);
}

Note: incrementAndGet() adds 1 to the index i element of the long array atomically and returns the value after adding 1.

The source code of addAndGet() is as follows:

public long addAndGet(int i, long delta) {
    // Check if the array is out of bounds
    long offset = checkedByteOffset(i);
    while (true) {
        // Gets the original value of the index offset for a long array
        long current = getRaw(offset);
        //Modify long-type values
        long next = current + delta;
        // Update the value of index offset for long arrays through CAS.
        if (compareAndSetRaw(offset, current, next))
            return next;
    }
}

Note: addAndGet() first checks if the array is out of bounds. If there is no boundary crossing, the value of array index i is obtained first, and then the value of i is updated by CAS function.

The getRaw() source code is as follows:

private long getRaw(long offset) {
    return unsafe.getLongVolatile(array, offset);
}

Note: unsafe is an Unsafe object returned through Unsafe.getUnsafe(). The elements of long array are atomized by Unsafe CAS function. For example, compareAndSetRaw() is a CAS function called Unsafe. Its source code is as follows:

private boolean compareAndSetRaw(long offset, long expect, long update) {
    return unsafe.compareAndSwapLong(array, offset, expect, update);
}

 

AtomicLong Array example

 Source code of 1//LongArrayTest.java
 2 import java.util.concurrent.atomic.AtomicLongArray;
 3 
 4 public class LongArrayTest {
 5     
 6     public static void main(String[] args){
 7 
 8//New AtomicLongArray object
 9         long[] arrLong = new long[] {10, 20, 30, 40, 50};
10         AtomicLongArray ala = new AtomicLongArray(arrLong);
11 
12         ala.set(0, 100);
13         for (int i=0, len=ala.length(); i<len; i++) 
14             System.out.printf("get(%d) : %s\n", i, ala.get(i));
15 
16         System.out.printf("%20s : %s\n", "getAndDecrement(0)", ala.getAndDecrement(0));
17         System.out.printf("%20s : %s\n", "decrementAndGet(1)", ala.decrementAndGet(1));
18         System.out.printf("%20s : %s\n", "getAndIncrement(2)", ala.getAndIncrement(2));
19         System.out.printf("%20s : %s\n", "incrementAndGet(3)", ala.incrementAndGet(3));
20 
21         System.out.printf("%20s : %s\n", "addAndGet(100)", ala.addAndGet(0, 100));
22         System.out.printf("%20s : %s\n", "getAndAdd(100)", ala.getAndAdd(1, 100));
23 
24         System.out.printf("%20s : %s\n", "compareAndSet()", ala.compareAndSet(2, 31, 1000));
25         System.out.printf("%20s : %s\n", "get(2)", ala.get(2));
26     }
27 }

Operation results:

get(0) : 100
get(1) : 20
get(2) : 30
get(3) : 40
get(4) : 50
  getAndDecrement(0) : 100
  decrementAndGet(1) : 19
  getAndIncrement(2) : 30
  incrementAndGet(3) : 41
      addAndGet(100) : 199
      getAndAdd(100) : 19
     compareAndSet() : true
              get(2) : 1000

Posted by Telemachus on Sat, 22 Dec 2018 21:15:05 -0800