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 tooldValue.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 allownullkeys 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.
