Skip to content

Commit 908934c

Browse files
committed
feat(ConcurrentHashMapDemo1): add demo of thread-safe updates with ConcurrentHashMap
What - Added ConcurrentHashMapDemo1 class. - Created ConcurrentHashMap<String,Integer> with initial capacity 16. - Spawned two threads: - Thread t1 inserts 1000 key-value pairs: "A0"→0 through "A999"→999 using put(). - Thread t2 runs 1000 iterations, computing keys "B"+(i % 500): • Uses computeIfAbsent to insert 500 distinct keys "B0".."B499". • Each key attempted twice; only the first attempt succeeds. - Used CountDownLatch to wait for both threads to finish. - Printed expected size (1500), actual size, and sample lookups for "A10" and "B10". Why - Demonstrates concurrent modifications to a map without external synchronization. - Shows thread-safety of ConcurrentHashMap for both put() and computeIfAbsent(). - Explains that computeIfAbsent is atomic per key, ensuring no race conditions. - Highlights that iteration order is not preserved in ConcurrentHashMap. How - map.put(key,val) directly inserts/updates entries. - map.computeIfAbsent(key, mappingFunction): - Executes mapping function only if key is absent. - Ensures atomic insertion per key. - CountDownLatch ensures main thread waits until both workers complete. - map.size() checked after inserts. Logic - Inputs: - Thread t1: 1000 unique "A"-keys. - Thread t2: 1000 iterations but only 500 unique "B"-keys due to modulo. - Outputs: - Expected map size = 1500. - Actual map size printed to verify correctness. - Example lookups for A10 and B10. - Flow: 1. Initialize map and latch. 2. Start t1 (inserts A-keys). 3. Start t2 (inserts B-keys). 4. Await latch. 5. Print stats. - Edge cases: - If threads didn’t synchronize properly, race conditions could reduce size. - ConcurrentHashMap guarantees correctness. - Complexity: - put() and computeIfAbsent() average O(1). - Total inserts ~1500 operations. - Concurrency: - Safe multi-threaded access without explicit locks. - computeIfAbsent ensures mapping function executes once per key. Real-life applications - Maintaining thread-safe caches where values are lazily initialized. - High-concurrency scenarios like counting, logging, or aggregating data. - Replacement for synchronized maps when scalability is critical. Notes - ConcurrentHashMap distributes updates across internal buckets for parallelism. - Initial capacity (16) grows dynamically. - Printed order is arbitrary since ConcurrentHashMap does not guarantee iteration order. Signed-off-by: https://github.com/Someshdiwan <[email protected]>
1 parent 413b14d commit 908934c

File tree

1 file changed

+56
-0
lines changed

1 file changed

+56
-0
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import java.util.concurrent.ConcurrentHashMap;
2+
import java.util.concurrent.CountDownLatch;
3+
4+
/**
5+
* Demonstrates concurrent updates to a ConcurrentHashMap using two threads.
6+
7+
* What this program does:
8+
* - Thread t1 inserts 1000 distinct keys: A0..A999 with values 0..999 via put (thread-safe).
9+
* - Thread t2 inserts up to 500 distinct keys: B0..B499 using computeIfAbsent, which is atomic per key.
10+
* It loops 1000 times, but because the key is computed as B+(i % 500), each B-key is attempted twice;
11+
* only the first attempt creates the mapping, the second is ignored since the key already exists.
12+
13+
* Therefore, the final map size is expected to be 1000 (A-keys) + 500 (B-keys) = 1500.
14+
15+
* Notes on concurrency behavior:
16+
* - ConcurrentHashMap allows high concurrency for reads and writes without locking the entire map.
17+
* - computeIfAbsent executes the mapping function at most once per key in a thread-safe manner.
18+
* - The map does not preserve the insertion order; the printed order is unspecified.
19+
*/
20+
21+
public class ConcurrentHashMapDemo1 {
22+
public static void main(String[] args) throws InterruptedException {
23+
// Initial capacity set to 16; CHM grows as needed. No need for extra synchronization.
24+
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>(16);
25+
26+
// Two threads will concurrently put values
27+
CountDownLatch latch = new CountDownLatch(2);
28+
29+
Thread t1 = new Thread(() -> {
30+
for (int i = 0; i < 1000; i++) {
31+
map.put("A" + i, i);
32+
}
33+
latch.countDown();
34+
}, "producer-A");
35+
36+
Thread t2 = new Thread(() -> {
37+
for (int i = 0; i < 1000; i++) {
38+
// computeIfAbsent is atomic per key
39+
final int val = i; // effectively final for lambda capture
40+
map.computeIfAbsent("B" + (i % 500), k -> val);
41+
}
42+
latch.countDown();
43+
}, "producer-B");
44+
45+
t1.start();
46+
t2.start();
47+
// Wait until both threads finish inserting
48+
latch.await();
49+
50+
int expectedSize = 1000 + 500;
51+
System.out.println("Expected size: " + expectedSize);
52+
System.out.println("Actual size: " + map.size());
53+
System.out.println("Sample get A10: " + map.get("A10"));
54+
System.out.println("Sample get B10: " + map.get("B10"));
55+
}
56+
}

0 commit comments

Comments
 (0)