Fading Coder

One Final Commit for the Last Sprint

Home > Notes > Content

Java Atomic Operations with AtomicLong in Multithreaded Concurrency

Notes 1

AtomicLong belongs to Java's core atomic classes, designed to handle atomic operations on long integer values, ensuring thread safety without explicit synchronization.

Key Methods of AtomicLong

// Creates an AtomicLong instance initialized to 0
AtomicLong()
// Creates an AtomicLong instance with a specified initial value
AtomicLong(long initialValue)
// Atomically updates the stored value to newValue
final void set(long newValue)
// Retrieves the current stored value
final long get()
// Atomically decrements the value by 1 and returns the new value
final long decrementAndGet()
// Atomically decrements the value by 1 and returns the old value
final long getAndDecrement()
// Atomically increments the value by 1 and returns the new value
final long incrementAndGet()
// Atomically increments the value by 1 and returns the old value
final long getAndIncrement()
// Atomically adds a delta to the value and returns the new value
final long addAndGet(long delta)
// Atomically adds a delta to the value and returns the old value
final long getAndAdd(long delta)
// Atomically updates the value to update if it equals expect; returns success status
final boolean compareAndSet(long expect, long update)
// Atomically updates the value to newValue and returns the old value
final long getAndSet(long newValue)
// Returns the stored value as an int
int intValue()
// Returns the stored value as a long
long longValue()
// Returns the stored value as a float
float floatValue()
// Returns the stored value as a double
double doubleValue()
// Lazily updates the value, with a slight delay but lower overhead for non-immediate reads
final void lazySet(long newValue)
// Spurious-failure prone version of compareAndSet, with no ordering guarantees
final boolean weakCompareAndSet(long expect, long update)

AtomicLong Source Code Analysis (JDK 1.7.0_40)

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

public class AtomicLong extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 1927816293512124184L;

    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;
    static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();

    private static native boolean VMSupportsCS8();

    static {
        try {
            valueOffset = unsafe.objectFieldOffset(
                AtomicLong.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile long value;

    public AtomicLong(long initialValue) {
        value = initialValue;
    }

    public AtomicLong() {}

    public final long get() {
        return value;
    }

    public final void set(long newValue) {
        value = newValue;
    }

    public final void lazySet(long newValue) {
        unsafe.putOrderedLong(this, valueOffset, newValue);
    }

    public final long getAndSet(long newValue) {
        while (true) {
            long current = get();
            if (compareAndSet(current, newValue))
                return current;
        }
    }

    public final boolean compareAndSet(long expect, long update) {
        return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
    }

    public final boolean weakCompareAndSet(long expect, long update) {
        return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
    }

    public final long getAndIncrement() {
        while (true) {
            long current = get();
            long next = current + 1;
            if (compareAndSet(current, next))
                return current;
        }
    }

    public final long getAndDecrement() {
        while (true) {
            long current = get();
            long next = current - 1;
            if (compareAndSet(current, next))
                return current;
        }
    }

    public final long getAndAdd(long delta) {
        while (true) {
            long current = get();
            long next = current + delta;
            if (compareAndSet(current, next))
                return current;
        }
    }

    public final long incrementAndGet() {
        for (;;) {
            long current = get();
            long next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

    public final long decrementAndGet() {
        for (;;) {
            long current = get();
            long next = current - 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

    public final long addAndGet(long delta) {
        for (;;) {
            long current = get();
            long next = current + delta;
            if (compareAndSet(current, next))
                return next;
        }
    }

    public String toString() {
        return Long.toString(get());
    }

    public int intValue() {
        return (int)get();
    }

    public long longValue() {
        return get();
    }

    public float floatValue() {
        return (float)get();
    }

    public double doubleValue() {
        return (double)get();
    }
}

The incrementAndGet() method is a typical example of how AtomicLong achieves atomciity:

public final long incrementAndGet() {
    for (;;) {
        // Read the current volatile value
        long current = get();
        // Calculate the updated value
        long next = current + 1;
        // Attempt atomic update using CAS
        if (compareAndSet(current, next))
            return next;
    }
}

How It Works:

  1. The get() method retrieves the current value of value, a volatile field ensuring visibility across threads.
  2. compareAndSet() invokes Unsafe.compareAndSwapLong, an intrinsic method that atomically compares the current memory value at valueOffset with the expected current value. If they match, it updates the value to next;
  3. If the CAS operation fails (due to concurrent updates by other threads), the loop repeats until the update succeeds.

Related Articles

Designing Alertmanager Templates for Prometheus Notifications

How to craft Alertmanager templates to format alert messages, improving clarity and presentation. Alertmanager uses Go’s text/template engine with additional helper functions. Alerting rules referenc...

Deploying a Maven Web Application to Tomcat 9 Using the Tomcat Manager

Tomcat 9 does not provide a dedicated Maven plugin. The Tomcat Manager interface, however, is backward-compatible, so the Tomcat 7 Maven Plugin can be used to deploy to Tomcat 9. This guide shows two...

Skipping Errors in MySQL Asynchronous Replication

When a replica halts because the SQL thread encounters an error, you can resume replication by skipping the problematic event(s). Two common approaches are available. Methods to Skip Errors 1) Skip a...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.