Welcome back! In previous lessons, you've learned about advanced data-sharing concepts and synchronization techniques. Today, we will expand on those foundations by delving into implementing a concurrent inventory system using ConcurrentHashMap
. This lesson will help you understand how to build thread-safe applications, a crucial skill in Java multi-threaded programming.
In this lesson, you'll gain the ability to:
- Utilize
ConcurrentHashMap
for managing data in a multi-threaded environment. - Perform atomic operations to safely manipulate shared data.
- Implement thread-safe inventory systems where multiple threads can modify data concurrently without conflicts.
By the end of this lesson, you will have the skills to manage shared data in multi-threaded systems without relying on locks, increasing efficiency and safety.
Let's start by looking at the InventorySystem
class, which manages the inventory of items.
Java1import java.util.concurrent.ConcurrentHashMap; 2 3public class InventorySystem { 4 private ConcurrentHashMap<String, Integer> inventory = new ConcurrentHashMap<>(); 5 6 // Adding an item to the inventory with atomic operations 7 public void addItem(String item, int quantity) { 8 inventory.merge(item, quantity, Integer::sum); 9 } 10 11 // Removing an item from the inventory while ensuring quantities stay valid 12 public void removeItem(String item, int quantity) { 13 inventory.computeIfPresent(item, (key, val) -> val - quantity > 0 ? val - quantity : null); 14 } 15 16 // Retrieving the current quantity of a specific item 17 public int getQuantity(String item) { 18 return inventory.getOrDefault(item, 0); 19 } 20 21 // Displaying all items and their quantities 22 public void displayInventory() { 23 inventory.forEach((key, value) -> System.out.println(key + ": " + value)); 24 } 25}
Let's go through each method in detail.
The addItem()
method uses the merge
function to ensure atomic updates. This method either adds a new item to the inventory or updates an existing one by adding the given quantity. The operation is thread-safe, which means multiple threads can modify the inventory concurrently without causing conflicts.
The removeItem()
method uses computeIfPresent()
to safely decrease the quantity of an item. If an item's quantity drops to zero, it’s automatically removed from the inventory. This approach prevents negative quantities and keeps the system clean and error-free, even when multiple threads interact with the inventory at once.
The getQuantity()
method provides a way to retrieve the current quantity of a specific item. It uses getOrDefault()
to ensure that the method returns 0
if the item doesn’t exist in the inventory. This ensures that no null
values are returned, maintaining robustness when used in a multi-threaded environment.
The displayInventory()
method uses forEach()
to safely iterate through all items in the inventory, printing their names and quantities. This method provides a consistent and up-to-date view of the inventory across multiple threads.
Now, let's see how multiple threads interact with this inventory system in practice.
Java1public class Main { 2 public static void main(String[] args) throws InterruptedException { 3 InventorySystem inventory = new InventorySystem(); 4 5 Thread t1 = new Thread(() -> { 6 inventory.addItem("Apple", 50); 7 inventory.addItem("Banana", 30); 8 }); 9 10 Thread t2 = new Thread(() -> { 11 inventory.removeItem("Apple", 20); 12 inventory.addItem("Orange", 40); 13 }); 14 15 t1.start(); 16 t2.start(); 17 18 t1.join(); 19 t2.join(); 20 21 inventory.displayInventory(); 22 } 23}
In this example, two threads (t1
and t2
) are working on the inventory simultaneously:
- Thread 1 adds 50 "Apples" and 30 "Bananas."
- Thread 2 removes 20 "Apples" and adds 40 "Oranges."
This shows how ConcurrentHashMap
handles these operations safely without needing explicit locks.
We use the join()
method to ensure that the main thread waits for both t1
and t2
to complete before displaying the final inventory. This guarantees that all changes made by the threads are reflected when the displayInventory()
method is called.
The ability to manage collections in a thread-safe manner is crucial for developing responsive and robust applications. Using ConcurrentHashMap
provides several benefits:
- Enhances performance: Avoids the bottleneck caused by locks and reduces contention between threads.
- Ensures consistency: Allows atomic updates that maintain data integrity across multiple threads.
- Real-world scenarios: Commonly used in multi-user applications or online stores where concurrent data access is necessary.
By mastering this, you will be able to design highly scalable systems that can handle concurrent data access efficiently.
Let’s move on to the practice section and apply these concepts!