Open In App

Effective Utilization of TreeMap in Concurrent Java Applications

A Java application may run many threads concurrently thanks to multithreading. Thread safety must be guaranteed when utilizing data structures like TreeMap in a multithreaded environment to prevent race situations. The recommended techniques for using TreeMap in multithreaded applications and avoiding race situations are covered in this tutorial.

TreeMap in a Multithreaded Environment

Thread safety is not inherent to TreeMap. Multiple threads altering a TreeMap concurrently may result in race situations and unexpected behavior. You may deal with this by using thread-safe alternatives such as ConcurrentNavigableMap, which is part of the java.util.concurrent package, or by using synchronization techniques.



Best Practices

Java Program to Use TreeMap in Multithreaded Applications Using Synchronization

Below is the code implementation to use TreeMap in multithreaded applications and avoid race conditions.

1. Using Locks for Thread-Safe TreeMap

The main issue with utilizing TreeMap in a multithreaded context is the potential for race situations when many threads attempt to edit the map at the same time. Locks are one way to get around this.






// Java program to Demonstrate Thread-Safe
// TreeMap Using ReadWriteLock
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
  
// Driver Class
public class ThreadSafeTreeMapExample 
{
    // ReadWriteLock for synchronization
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    // TreeMap to store key-value pairs
    private final Map<String, Integer> threadSafeTreeMap = new TreeMap<>();
  
    // Method to add key-value pairs to the TreeMap
    public void addToMap(String key, int value) 
    {
          // Acquire write lock
        lock.writeLock().lock(); 
        try{
              // Add key-value pair
            threadSafeTreeMap.put(key, value); 
            System.out.println("Added: " + key + ", " + value);
        } finally {
              // Release write lock
            lock.writeLock().unlock(); 
        }
    }
  
    // Method to get value for a given key from the TreeMap
    public int getFromMap(String key) 
    {
          // Acquire read lock
        lock.readLock().lock(); 
        try {
             // Get value for key or return default value (-1)
            return threadSafeTreeMap.getOrDefault(key, -1); 
        } finally {
              // Release read lock
            lock.readLock().unlock(); 
        }
    }
  
      // Main Function
    public static void main(String[] args) 
    {
        ThreadSafeTreeMapExample example = new ThreadSafeTreeMapExample();
  
        // Example usage: add key-value pairs in separate threads
        new Thread(() -> example.addToMap("A", 1)).start();
        new Thread(() -> example.addToMap("B", 2)).start();
  
        // Display values for keys "A" and "B"
        System.out.println("Value for A: " + example.getFromMap("A"));
        System.out.println("Value for B: " + example.getFromMap("B"));
    }
}

Output
Added: A, 1
Added: B, 2
Value for A: 1
Value for B: 2



Explanation of the above Program:

2. Using ConcurrentHashMap with TreeMap

Combining a ConcurrentHashMap with a TreeMap is an additional strategy. This leverages ConcurrentHashMap’s concurrent writing capabilities while enabling thread-safe read operations on the TreeMap.




// Java Program to Utilize of TreeMap
// in Concurrent Java Applications
// Using ConcurrentHashMap with TreeMap
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
  
// Driver Class
public class ConcurrentMapWithTreeMapExample {
    // ConcurrentSkipListMap for concurrent access
    private final ConcurrentSkipListMap<String, Integer>
        concurrentMap = new ConcurrentSkipListMap<>();
    // ExecutorService for asynchronous execution
    private final ExecutorService executorService
        = Executors.newFixedThreadPool(2);
  
    // Method to add key-value pair to the concurrent map
    // asynchronously
    public void addToMap(String key, int value)
    {
        CompletableFuture.runAsync(() -> {
            addEntryToMap(key, value);
        }, executorService);
    }
  
    // Method to get value for a given key from the
    // concurrent map
    public int getFromMap(String key)
    {
        return concurrentMap.getOrDefault(key, -1);
    }
  
    // Method to get an ordered copy of the concurrent map
    public ConcurrentSkipListMap<String, Integer> getOrderedMap()
    {
        return new ConcurrentSkipListMap<>(concurrentMap);
    }
  
    // Private method to add entry to the concurrent map
    private void addEntryToMap(String key, int value)
    {
        concurrentMap.put(key, value);
        System.out.println("Added: " + key + ", " + value);
    }
  
    // Main Function
    public static void main(String[] args)
    {
        ConcurrentMapWithTreeMapExample example
            = new ConcurrentMapWithTreeMapExample();
  
        // Add key-value pairs asynchronously
        CompletableFuture<Void> futureA
            = CompletableFuture.runAsync(()
                    -> example.addToMap("A", 1),
                example.executorService);
          
          CompletableFuture<Void> futureB
            = CompletableFuture.runAsync(()
                    -> example.addToMap("B", 2),
                example.executorService);
  
        // Wait for all tasks to complete
        CompletableFuture.allOf(futureA, futureB).join();
  
        // Display values for keys "A" and "B"
        System.out.println("Value for A: " + example.getFromMap("A"));
        System.out.println("Value for B: " + example.getFromMap("B"));
  
        // Get an ordered copy of the concurrent map
        ConcurrentSkipListMap<String, Integer> orderedMap
            = example.getOrderedMap();
        
        System.out.println("Ordered Map: " + orderedMap);
  
        // Shut down the executor service
        try {
            example.executorService.shutdown();
        }
        finally {
        }
    }
}

Output
Value for A: 1
Value for B: 2
Ordered Map: {A=1, B=2}
Added: B, 2
Added: A, 1



Explanation of the above Program:


Article Tags :