How do I use Atomic variables from java.util.concurrent.atomic?

Atomic variables in java.util.concurrent.atomic are designed for lock-free, thread-safe operations on single variables. They leverage low-level CPU instructions like Compare-And-Swap (CAS) to ensure data consistency without the overhead of synchronized blocks.

Here’s a guide on how to use the most common atomic classes.

1. AtomicInteger and AtomicLong

These are used for numeric counters or IDs. Instead of using ++ (which is not atomic), you use methods like incrementAndGet().

package org.kodejava.util.concurrent;

import java.util.concurrent.atomic.AtomicInteger;

public class CounterExample {
    private final AtomicInteger counter = new AtomicInteger(0);

    public void increment() {
        // Atomically increments by one and returns the new value
        int newValue = counter.incrementAndGet();
        System.out.println("Current Value: " + newValue);
    }

    public int getValue() {
        return counter.get();
    }
}

2. AtomicBoolean

Useful for flags that need to be checked and updated across threads, such as a “running” state.

package org.kodejava.util.concurrent;

import java.util.concurrent.atomic.AtomicBoolean;

public class Worker {
    private final AtomicBoolean initialized = new AtomicBoolean(false);

    public void init() {
        // compareAndSet(expectedValue, newValue)
        // Only sets to true if it was currently false
        if (initialized.compareAndSet(false, true)) {
            System.out.println("Performing one-time initialization...");
        }
    }
}

3. AtomicReference

Used to wrap any object reference. This is great for implementing non-blocking algorithms where you need to update an entire object state atomically.

package org.kodejava.util.concurrent;

import java.util.concurrent.atomic.AtomicReference;

public class StateManager {
    private final AtomicReference<String> status = new AtomicReference<>("IDLE");

    public void updateStatus(String oldStatus, String newStatus) {
        boolean success = status.compareAndSet(oldStatus, newStatus);
        if (success) {
            System.out.println("Status changed to: " + newStatus);
        }
    }
}

4. Advanced Accumulators (LongAdder)

If you have a very high-contention environment (many threads constantly updating a sum), LongAdder is generally faster than AtomicLong because it maintains internal cells to reduce contention.

package org.kodejava.util.concurrent;

import java.util.concurrent.atomic.LongAdder;

public class HighContentionCounter {
    private final LongAdder adder = new LongAdder();

    public void add() {
        adder.increment();
    }

    public long getTotal() {
        return adder.sum();
    }
}

Key Methods to Remember

  • get() / set(): Read or write the value (similar to volatile).
  • lazySet(): Eventually sets the value; faster but doesn’t guarantee immediate visibility to other threads.
  • compareAndSet(expect, update): The heart of atomic variables. Updates only if the current value matches expect.
  • getAndAccumulate(delta, accumulatorFunction): (Java 8+) Allows complex atomic updates using a Lambda.

When to use them?

  • Use them for simple counters, sequence generators, or flags.
  • Avoid them if you need to update multiple dependent variables at once; in that case, a ReentrantLock or synchronized block is safer to ensure the entire operation is atomic.

How do I decrypt an object with DES?

On the previous example How do I encrypt an object with DES? we encrypt an object. In this example we will decrypt the stored object.

package org.kodejava.crypto;

import java.io.*;

import javax.crypto.SecretKey;
import javax.crypto.Cipher;
import javax.crypto.SealedObject;

public class ObjectDecrypt {
    public static void main(String[] args) throws Exception {
        // Read the previously stored SecretKey.
        SecretKey key = (SecretKey) readFromFile("secretkey.dat");

        // Read the SealedObject
        SealedObject sealedObject = (SealedObject) readFromFile("sealed.dat");

        // Preparing Cipher object from decryption.
        if (sealedObject != null) {
            String algorithmName = sealedObject.getAlgorithm();

            Cipher cipher = Cipher.getInstance(algorithmName);
            cipher.init(Cipher.DECRYPT_MODE, key);

            String text = (String) sealedObject.getObject(cipher);
            System.out.println("Text = " + text);
        }
    }

    // Method for reading object stored in a file.
    private static Object readFromFile(String filename) {
        try (FileInputStream fis = new FileInputStream(filename);
             ObjectInputStream ois = new ObjectInputStream(fis)) {

            return ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

How do I encrypt an object with DES?

This example demonstrate encrypting an object using DES algorithm. After the object was encrypted we store it in a file which will be decrypted in the next example here How do I decrypt an object with DES.

package org.kodejava.crypto;

import java.io.*;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SealedObject;
import javax.crypto.SecretKey;

public class ObjectEncrypt {
    public static void main(String[] args) throws Exception {
        // Generating a temporary key and store it in a file.
        SecretKey key = KeyGenerator.getInstance("DES").generateKey();
        writeToFile("secretkey.dat", key);

        // Preparing Cipher object for encryption.
        Cipher cipher = Cipher.getInstance("DES");
        cipher.init(Cipher.ENCRYPT_MODE, key);

        // Here we seal (encrypt) a simple string message (a string object).
        SealedObject sealedObject = new SealedObject("THIS IS A SECRET MESSAGE!", cipher);

        // Write the object out as a binary file.
        writeToFile("sealed.dat", sealedObject);
    }

    // Store object in a file for future use.
    private static void writeToFile(String filename, Object object) throws Exception {
        try (FileOutputStream fos = new FileOutputStream(filename);
             ObjectOutputStream oos = new ObjectOutputStream(fos)) {

            oos.writeObject(object);
        }
    }
}