ConcurrentMap interface and ConcurrentHashMap class

In concurrent programming, Java provides thread-safe collection classes to handle shared data without compromising integrity or performance. The ConcurrentMap interface and ConcurrentHashMap class are vital for achieving thread safety in a concurrent environment.

1. ConcurrentMap Interface

The ConcurrentMap<K, V> is part of the java.util.concurrent package and extends the Map<K, V> interface. It introduces atomic operations to modify the map concurrently.

Important Methods in ConcurrentMap:

  • V putIfAbsent(K key, V value): Puts the key-value pair only if the key is not already associated.
  • boolean remove(Object key, Object value): Removes the key only if it’s mapped to the given value.
  • boolean replace(K key, V oldValue, V newValue): Replaces the value for a key only if currently mapped to oldValue.
  • V replace(K key, V value): Replaces the value for the key unconditionally if present.

Features:

  • Ensures atomicity and consistency during modifications.
  • Thread-safe: can be accessed by multiple threads simultaneously without explicit synchronization.

 2. ConcurrentHashMap Class

ConcurrentHashMap<K, V> is a thread-safe implementation of the ConcurrentMap interface. It is part of java.util.concurrent and is optimized for high-concurrency scenarios.

Important Characteristics:

  • No null keys or values: Unlike HashMap, it does not allow null keys or values.
  • Segmented locking (Java 7 and earlier): Internally divided into segments (buckets) to reduce lock contention.
  • CAS (Compare-And-Swap) and synchronized blocks (Java 8+): Better performance through lock stripping and non-blocking algorithms.
  • Constructors:
 
ConcurrentHashMap<>()
ConcurrentHashMap<>(int initialCapacity)
ConcurrentHashMap<>(int initialCapacity, float loadFactor)
ConcurrentHashMap<>(int initialCapacity, float loadFactor, int concurrencyLevel)
Code language: HTML, XML (xml)

Program-1

This Java program demonstrates the use of the ConcurrentMap interface and its implementation using ConcurrentHashMap to manage and manipulate a map of river names and their lengths in a thread-safe manner.

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class ConcurrentMapDemo {
    public static void main(String[] args) {
        ConcurrentMap<String, Integer> riverLengths = new ConcurrentHashMap<>();
        riverLengths.putIfAbsent("Ganges", 2525);
        riverLengths.putIfAbsent("Yamuna", 1376);
        riverLengths.putIfAbsent("Brahmaputra", 2900);
        riverLengths.putIfAbsent("Godavari", 1465);
        riverLengths.putIfAbsent("Krishna", 1400);
        System.out.println("Initial river lengths: " + riverLengths);
        riverLengths.putIfAbsent("Ganges", 2500);
        System.out.println("After putIfAbsent with duplicate key: " + riverLengths);
        riverLengths.replace("Yamuna", 1376, 1370);
        System.out.println("After replace: " + riverLengths);
        riverLengths.remove("Godavari", 1465);
        System.out.println("After remove: " + riverLengths);
        // Demonstrate thread-safe updates
        Runnable task = () -> {
            for (int i = 0; i < 10; i++) {
                riverLengths.merge("Ganges", 10, Integer::sum);
            }
        };
        // Create multiple threads to update the map
        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        Thread t3 = new Thread(task);
        t1.start();
        t2.start();
        t3.start();
        try {
            t1.join();
            t2.join();
            t3.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Final river lengths after concurrent updates: " + riverLengths);
    }
}
/*
C:\>javac ConcurrentMapDemo.java
C:\>java ConcurrentMapDemo
Initial river lengths: {Brahmaputra=2900, Ganges=2525, Krishna=1400, Yamuna=1376, Godavari=1465}
After putIfAbsent with duplicate key: {Brahmaputra=2900, Ganges=2525, Krishna=1400, Yamuna=1376, Godavari=1465}
After replace: {Brahmaputra=2900, Ganges=2525, Krishna=1400, Yamuna=1370, Godavari=1465}
After remove: {Brahmaputra=2900, Ganges=2525, Krishna=1400, Yamuna=1370}
Final river lengths after concurrent updates: {Brahmaputra=2900, Ganges=2825, Krishna=1400, Yamuna=1370}
*/

Program-2

This Java program illustrates the use of the ConcurrentMap interface and the ConcurrentHashMap class to simulate a thread-safe message-passing system between senders and recipients. It showcases concurrent operations where multiple threads simultaneously send and receive messages without causing data corruption or race conditions.

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class ConcurrentMessageSystem {
    private static final ConcurrentMap<String, String> messageQueue = new ConcurrentHashMap<>();
    // Method to send a message
    public static void sendMessage(String recipient, String message) {
        messageQueue.put(recipient, message);
        System.out.println("Message sent to " + recipient + ": " + message);
    }
    // Method to receive a message
    public static String receiveMessage(String recipient) {
        String message = messageQueue.remove(recipient);
        if (message != null) {
            System.out.println("Message received by " + recipient + ": " + message);
        } else {
            System.out.println("No message for " + recipient);
        }
        return message;
    }
    public static void main(String[] args) {
        // Create multiple threads to send and receive messages concurrently
        Thread sender1 = new Thread(() -> sendMessage("Mahesh", "Hello Mahesh!"));
        Thread sender2 = new Thread(() -> sendMessage("Paani", "Hey Paani, how's it going?"));
        Thread receiver1 = new Thread(() -> receiveMessage("Mahesh"));
        Thread receiver2 = new Thread(() -> receiveMessage("Paani"));
        // Start threads
        sender1.start();
        sender2.start();
        receiver1.start();
        receiver2.start();
        // Wait for threads to finish
        try {
            sender1.join();
            sender2.join();
            receiver1.join();
            receiver2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
         }
    }
}

/*
C:\>javac ConcurrentMessageSystem.java
C:\>java ConcurrentMessageSystem
Message received by Mahesh: Hello Mahesh!
Message sent to Paani: Hey Paani, how's it going?
Message received by Paani: Hey Paani, how's it going?
Message sent to Mahesh: Hello Mahesh!
*/

Program-3

This Java program simulates a thread-safe ticket reservation system for darshan bookings. It handles multiple devotees trying to reserve limited tickets concurrently, ensuring that the reservation logic is atomic and race-condition-free using ConcurrentHashMap and AtomicInteger.

import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
public class DarshanReservationSystem {
    private static final int MAX_TICKETS = 3;
    private static final ConcurrentHashMap<String, Integer> ticketAvailability = new ConcurrentHashMap<>();
    private static final AtomicInteger totalReservedTickets = new AtomicInteger(0);
    static {
        // Initialize ticket availability
        ticketAvailability.put("DarshanTicket", MAX_TICKETS);
    }
    public static boolean reserveTicket(String DevoteeId) {
        // Check if tickets are available
        if (totalReservedTickets.get() >= MAX_TICKETS) {
            System.out.println("Sorry, all tickets have been reserved. Please try again later.");
            return false;
        }
        // Reserve ticket
        int remainingTickets = ticketAvailability.getOrDefault("DarshanTicket", 0);
        if (remainingTickets > 0) {
            int updatedRemainingTickets = ticketAvailability.compute("DarshanTicket", (k, v) -> v - 1);
            if (updatedRemainingTickets >= 0) {
                totalReservedTickets.incrementAndGet();
                System.out.println("Ticket reserved for Devotee: " + DevoteeId);
                return true;
            }
        }
        System.out.println("Sorry, failed to reserve ticket for Devotee: " + DevoteeId);
        return false;
    }
    public static void main(String[] args) {
        Random random = new Random();
        for (int i = 1; i <= 5; i++) {
            String DevoteeId = "Devotee" + i;
            Thread thread = new Thread(() -> {
                // Introduce random delay
                try {
                    Thread.sleep(random.nextInt(1000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                boolean success = reserveTicket(DevoteeId);
                if (!success) {
                    System.out.println("Devotee " + DevoteeId + " could not reserve a ticket.");
                }
            });
            thread.start();
        }
    }
}
/*
C:\>javac DarshanReservationSystem.java
C:\>java DarshanReservationSystem
Ticket reserved for Devotee: Devotee5
Ticket reserved for Devotee: Devotee4
Ticket reserved for Devotee: Devotee1
Sorry, all tickets have been reserved. Please try again later.
Devotee Devotee3 could not reserve a ticket.
Sorry, all tickets have been reserved. Please try again later.
Devotee Devotee2 could not reserve a ticket.
*/

The DarshanReservationSystem program efficiently models a concurrent reservation system where limited resources (tickets) are accessed by multiple clients (devotees). By combining ConcurrentHashMap and AtomicInteger, it maintains consistency, avoids overbooking, and provides a strong foundation for real-world ticketing logic.

The ConcurrentMap interface and its primary implementation, ConcurrentHashMap, provide powerful tools for building high-performance, thread-safe collections in concurrent Java applications. They are essential when multiple threads need to access and modify a shared map without risking data inconsistency or race conditions.

Scroll to Top