🎓 A1Learner
🔍
0 of 30 lessons completed

Java Concurrent Collections

Concurrent collections are collections built for safe use by many threads at once. Their methods handle the locking, so threads can share one map or one queue directly.

Two threads fill one map

Each thread puts its own two keys into one ConcurrentHashMap. The main thread prints the entries sorted by key after joining both threads.

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
public class Main {
  public static void main(String[] args) throws Exception {
    ConcurrentHashMap<String, Integer> counts = new ConcurrentHashMap<>();
    Thread first = new Thread(() -> {
      counts.put("apple", 3);
      counts.put("banana", 2);
    });
    Thread second = new Thread(() -> {
      counts.put("cherry", 5);
      counts.put("date", 1);
    });
    first.start();
    second.start();
    first.join();
    second.join();
    List<String> keys = new ArrayList<>(counts.keySet());
    Collections.sort(keys);
    for (String key : keys) {
      System.out.println(key + " = " + counts.get(key));
    }
  }
}

putIfAbsent keeps the first value

putIfAbsent returns the value already present and leaves it in place. The printed pair shows the first value survived the second call.

import java.util.concurrent.ConcurrentHashMap;
public class Main {
  public static void main(String[] args) {
    ConcurrentHashMap<String, Integer> counts = new ConcurrentHashMap<>();
    counts.put("apple", 3);
    Integer previous = counts.putIfAbsent("apple", 99);
    System.out.println("Returned: " + previous);
    System.out.println("Stored: " + counts.get("apple"));
  }
}

A queue drained and sorted

Two producers offer fixed values into one ConcurrentLinkedQueue. The main thread drains the queue after joining them, then sorts and prints the values.

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
public class Main {
  public static void main(String[] args) throws Exception {
    ConcurrentLinkedQueue<Integer> queue = new ConcurrentLinkedQueue<>();
    Thread first = new Thread(() -> {
      queue.offer(30);
      queue.offer(10);
    });
    Thread second = new Thread(() -> {
      queue.offer(20);
      queue.offer(40);
    });
    first.start();
    second.start();
    first.join();
    second.join();
    List<Integer> drained = new ArrayList<>();
    Integer value;
    while ((value = queue.poll()) != null) {
      drained.add(value);
    }
    Collections.sort(drained);
    for (int number : drained) {
      System.out.println(number);
    }
  }
}

Example

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
public class Main {
  public static void main(String[] args) throws Exception {
    ConcurrentHashMap<String, Integer> scores = new ConcurrentHashMap<>();
    ConcurrentLinkedQueue<String> finished = new ConcurrentLinkedQueue<>();
    Thread first = new Thread(() -> {
      scores.put("ana", 90);
      scores.put("bo", 85);
      finished.offer("ana");
      finished.offer("bo");
    });
    Thread second = new Thread(() -> {
      scores.put("cy", 92);
      scores.put("di", 88);
      finished.offer("cy");
      finished.offer("di");
    });
    first.start();
    second.start();
    first.join();
    second.join();
    List<String> keys = new ArrayList<>(scores.keySet());
    Collections.sort(keys);
    for (String key : keys) {
      System.out.println(key + " = " + scores.get(key));
    }
    List<String> names = new ArrayList<>();
    String name;
    while ((name = finished.poll()) != null) {
      names.add(name);
    }
    Collections.sort(names);
    System.out.println("Finished: " + String.join(", ", names));
  }
}

Key points

  • A ConcurrentHashMap allows puts and gets from many threads at the same time.
  • Disjoint keys keep the final contents exact when threads fill one map.
  • putIfAbsent adds a value only when the key has none, as one atomic step.
  • A ConcurrentLinkedQueue accepts offers from many producer threads safely.
  • The main thread prints after joining the workers, never during the race.
  • A concurrent queue promises no useful print order. Its contents drain and sort before printing.

In this example, two threads fill one ConcurrentHashMap with disjoint keys and offer names into one ConcurrentLinkedQueue. The main thread joins both workers before printing anything. It prints the map sorted by key, then drains the queue, sorts the names, and prints them on one line.

Note

A ConcurrentHashMap never holds null keys or null values.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

Why does the program sort the drained queue values before printing them?