Fundamentals 10 min read

Mastering Thread‑Safe Objects in Java: From Synchronized Collections to Atomic Variables

This article explains how Java threads share objects, why immutable objects are safest, how to make mutable collections thread‑safe using synchronized wrappers or concurrent classes, handles non‑thread‑safe types like SimpleDateFormat, and demonstrates race‑condition fixes with synchronized blocks and AtomicInteger.

FunTester
FunTester
FunTester
Mastering Thread‑Safe Objects in Java: From Synchronized Collections to Atomic Variables

Thread‑Safe Objects

In Java, threads communicate by sharing references to the same objects. Concurrent reads and writes on mutable objects can lead to inconsistent state or unexpected results. The most reliable way to avoid these problems is to use immutable objects. When immutability is not feasible, the mutable objects must be made thread‑safe.

Synchronizing Collections

Standard collection classes (e.g., HashMap, ArrayList) keep internal mutable state. The simplest way to protect them is to wrap the collection with the synchronized utilities provided by java.util.Collections:

Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
List<Integer> list = Collections.synchronizedList(new ArrayList<>());

The wrapper synchronizes each individual method call, guaranteeing that only one thread can execute a method at a time. This prevents the collection from being left in an inconsistent state, but it also forces every operation—including reads—to acquire a lock.

Concurrent Collections for Read‑Heavy Workloads

When the workload performs many more reads than writes, the lock contention of synchronized wrappers becomes a bottleneck. Java provides lock‑free or low‑contention alternatives: CopyOnWriteArrayList – on each mutating operation a fresh copy of the underlying array is created. Reads are performed on an immutable snapshot, giving near‑zero contention. Writes are more expensive than Collections.synchronizedList, so this class is best when writes are rare. ConcurrentHashMap – internally partitions the map into segments (or uses a lock‑striping algorithm in newer JDKs) so that reads and writes on different keys can proceed concurrently. It outperforms Collections.synchronizedMap because it does not serialize all operations on a single lock.

CopyOnWriteArrayList<String> cowList = new CopyOnWriteArrayList<>();
ConcurrentHashMap<String, String> chm = new ConcurrentHashMap<>();

Handling Non‑Thread‑Safe Types (e.g., SimpleDateFormat)

Classes such as java.text.SimpleDateFormat maintain mutable internal state and are not safe for concurrent use. Sharing a single instance across threads can produce incorrect parsing or formatting.

Safe patterns include:

Instantiate a new SimpleDateFormat each time it is needed.

Store a ThreadLocal<SimpleDateFormat> so each thread has its own instance.

Synchronize access to a shared instance using the synchronized keyword or an explicit Lock.

These approaches apply to any mutable class that lacks built‑in thread safety.

Race Conditions

Illustrative Counter Example

class Counter {
    private int counter = 0;
    public void increment() { counter++; }
    public int getValue() { return counter; }
}

The counter++ operation consists of three steps: read the current value, add one, and write the new value back. When two threads execute increment() simultaneously, the steps can interleave, causing one increment to be lost and the final value to be 1 instead of 2.

Synchronized Solution

class SynchronizedCounter {
    private int counter = 0;
    public synchronized void increment() { counter++; }
    public synchronized int getValue() { return counter; }
}

Marking the methods as synchronized forces exclusive access to the critical section, eliminating the race at the cost of lock acquisition overhead.

Lock‑Free Atomic Variables

Java’s java.util.concurrent.atomic package provides lock‑free classes that perform atomic operations using low‑level CPU instructions.

AtomicInteger atomic = new AtomicInteger(3);
int newValue = atomic.incrementAndGet(); // atomically increments and returns the new value

Using AtomicInteger (or other atomic types) removes the need for explicit synchronization while delivering higher throughput.

Compound Operations on Collections

Problem: Non‑Atomic Sequences

Even when each collection method is synchronized, a sequence of calls is not atomic. For example:

List<String> list = Collections.synchronizedList(new ArrayList<>());
if (!list.contains("FunTester")) {
    list.add("FunTester");
}

Between the contains check and the add, another thread may modify the list, resulting in duplicate entries.

External Synchronization

Wrap the whole sequence in a synchronized block that locks on the collection object:

synchronized (list) {
    if (!list.contains("FunTester")) {
        list.add("FunTester");
    }
}

This guarantees that only one thread can execute the compound action at a time.

Built‑in Atomic Methods in ConcurrentHashMap

ConcurrentHashMap

supplies atomic compound operations that eliminate the need for external locking: putIfAbsent(key, value) inserts the value only if the key is not already present. computeIfAbsent(key, mappingFunction) computes a value lazily and inserts it atomically.

Map<String, String> map = new ConcurrentHashMap<>();
map.putIfAbsent("foo", "bar");
map.computeIfAbsent("foo", k -> k + "bar");

These methods are part of the Map interface and provide a concise, thread‑safe way to perform conditional updates.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaconcurrencySynchronizationthread safetyCollectionsAtomicInteger
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.