Thread-Safe Collection Implementations in Java

Standard Java collections like ArrayList and HashMap aren't thread-safe for concurrent access. Java provides specialized concurrent collections that offer better performance and safety in multi-threaded environments.

Standard Collection Thread-Safe Alternative
ArrayList CopyOnWriteArrayList
HashSet CopyOnWriteArraySet
HashMap ConcurrentHashMap

CopyOnWriteArrayList

This implementation is ideal for read-heavy scenarios where writes are infrequent. It maintains thread safety by creating a new copy of the underlying array on each modification.


public class ThreadSafeListExample {
    public static void main(String[] args) {
        List<String> safeList = new CopyOnWriteArrayList<>();
        
        // Multiple threads can safely access this list
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                safeList.add(Thread.currentThread().getName());
                System.out.println(safeList);
            }).start();
        }
    }
}

The implementation uses a ReentrantLock to ensure atomic writes:


public boolean add(E element) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] current = getArray();
        Object[] newArray = Arrays.copyOf(current, current.length + 1);
        newArray[current.length] = element;
        setArray(newArray);
        return true;
    } finally {
        lock.unlock();
    }
}

ConcurrentHashMap

This is the modern replacement for Hashtable, offering better performance through finer-grained locking.

JDK 8 implementation uses synchronized blocks on individual buckets rather than segment locks:


final V putVal(K key, V value, boolean onlyIfAbsent) {
    // Null checks omitted for brevity
    int hash = spread(key.hashCode());
    
    for (Node<K,V>[] tab = table;;) {
        Node<K,V> f; int n, i, fh;
        if (tab == null || (n = tab.length) == 0)
            tab = initTable();
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
            if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null)))
                break;
        }
        else {
            V oldVal = null;
            synchronized (f) {
                // Handle existing bucket
            }
        }
    }
    addCount(1L, binCount);
    return null;
}

The implementation uses several optimization:

  • CAS operations for empty buckets
  • Synchronized blocks for occupied buckets
  • Automatic resizing when needed
  • Tree conversion for long chains

For counting elements, it uses a striped counter approach to reduce contention:


private final void addCount(long x, int check) {
    CounterCell[] as;
    if ((as = counterCells) != null ||
        !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
        // Handle contention
    }
    // Potential resize handling
}

Tags: java ConcurrentHashMap CopyOnWriteArrayList ThreadSafety Collections

Posted on Sun, 17 May 2026 14:36:40 +0000 by sgarcia