diff --git a/core-java-modules/core-java-concurrency-collections-2/pom.xml b/core-java-modules/core-java-concurrency-collections-2/pom.xml new file mode 100644 index 0000000000..0e5310b9da --- /dev/null +++ b/core-java-modules/core-java-concurrency-collections-2/pom.xml @@ -0,0 +1,43 @@ + + 4.0.0 + com.baeldung.concurrent.lock + core-java-concurrency-collections-2 + 0.0.1-SNAPSHOT + + + 1.21 + 28.2-jre + + + + + com.google.guava + guava + ${guava.version} + + + org.openjdk.jmh + jmh-core + ${jmh.version} + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + + + + + src + + + maven-compiler-plugin + 3.8.0 + + 1.8 + 1.8 + + + + + \ No newline at end of file diff --git a/core-java-modules/core-java-concurrency-collections-2/src/main/java/com/baeldung/concurrent/lock/ConcurrentAccessBenchmark.java b/core-java-modules/core-java-concurrency-collections-2/src/main/java/com/baeldung/concurrent/lock/ConcurrentAccessBenchmark.java new file mode 100644 index 0000000000..ceb53ce077 --- /dev/null +++ b/core-java-modules/core-java-concurrency-collections-2/src/main/java/com/baeldung/concurrent/lock/ConcurrentAccessBenchmark.java @@ -0,0 +1,54 @@ +package com.baeldung.concurrent.lock; + +import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.Map; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +@State(Scope.Thread) +@Fork(value = 2) +@Warmup(iterations = 0) +public class ConcurrentAccessBenchmark { + static final int SLOTS = 4; + static final int THREADS = 10000; + static final int BUCKETS = Runtime.getRuntime().availableProcessors() * SLOTS; + SingleLock singleLock = new SingleLock(); + StripedLock stripedLock = new StripedLock(BUCKETS); + + @Benchmark + @BenchmarkMode(Mode.Throughput) + @OutputTimeUnit(TimeUnit.MILLISECONDS) + public Map singleLockHashMap() throws InterruptedException { + return singleLock.doWork(new HashMap(), THREADS, SLOTS); + } + + @Benchmark + @BenchmarkMode(Mode.Throughput) + @OutputTimeUnit(TimeUnit.MILLISECONDS) + public Map stripedLockHashMap() throws InterruptedException { + return stripedLock.doWork(new HashMap(), THREADS, SLOTS); + } + + @Benchmark + @BenchmarkMode(Mode.Throughput) + @OutputTimeUnit(TimeUnit.MILLISECONDS) + public Map singleLockConcurrentHashMap() throws InterruptedException { + return singleLock.doWork(new ConcurrentHashMap(), THREADS, SLOTS); + } + + @Benchmark + @BenchmarkMode(Mode.Throughput) + @OutputTimeUnit(TimeUnit.MILLISECONDS) + public Map stripedLockConcurrentHashMap() throws InterruptedException { + return stripedLock.doWork(new ConcurrentHashMap(), THREADS, SLOTS); + } +} diff --git a/core-java-modules/core-java-concurrency-collections-2/src/main/java/com/baeldung/concurrent/lock/ConcurrentAccessExperiment.java b/core-java-modules/core-java-concurrency-collections-2/src/main/java/com/baeldung/concurrent/lock/ConcurrentAccessExperiment.java new file mode 100644 index 0000000000..ec6d3895da --- /dev/null +++ b/core-java-modules/core-java-concurrency-collections-2/src/main/java/com/baeldung/concurrent/lock/ConcurrentAccessExperiment.java @@ -0,0 +1,26 @@ +package com.baeldung.concurrent.lock; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import com.google.common.base.Supplier; + +public abstract class ConcurrentAccessExperiment { + + public final Map doWork(Map map, int threads, int slots) { + CompletableFuture[] requests = new CompletableFuture[threads * slots]; + + for (int i = 0; i < threads; i++) { + requests[slots * i + 0] = CompletableFuture.supplyAsync(putSupplier(map, i)); + requests[slots * i + 1] = CompletableFuture.supplyAsync(getSupplier(map, i)); + requests[slots * i + 2] = CompletableFuture.supplyAsync(getSupplier(map, i)); + requests[slots * i + 3] = CompletableFuture.supplyAsync(getSupplier(map, i)); + } + CompletableFuture.allOf(requests).join(); + + return map; + } + + protected abstract Supplier putSupplier(Map map, int key); + protected abstract Supplier getSupplier(Map map, int key); +} \ No newline at end of file diff --git a/core-java-modules/core-java-concurrency-collections-2/src/main/java/com/baeldung/concurrent/lock/SingleLock.java b/core-java-modules/core-java-concurrency-collections-2/src/main/java/com/baeldung/concurrent/lock/SingleLock.java new file mode 100644 index 0000000000..4dff459df6 --- /dev/null +++ b/core-java-modules/core-java-concurrency-collections-2/src/main/java/com/baeldung/concurrent/lock/SingleLock.java @@ -0,0 +1,36 @@ +package com.baeldung.concurrent.lock; + +import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; + +import com.google.common.base.Supplier; + +public class SingleLock extends ConcurrentAccessExperiment { + ReentrantLock lock; + + public SingleLock() { + lock = new ReentrantLock(); + } + + protected Supplier putSupplier(Map map, int key) { + return (()-> { + lock.lock(); + try { + return map.put("key" + key, "value" + key); + } finally { + lock.unlock(); + } + }); + } + + protected Supplier getSupplier(Map map, int key) { + return (()-> { + lock.lock(); + try { + return map.get("key" + key); + } finally { + lock.unlock(); + } + }); + } +} diff --git a/core-java-modules/core-java-concurrency-collections-2/src/main/java/com/baeldung/concurrent/lock/StripedLock.java b/core-java-modules/core-java-concurrency-collections-2/src/main/java/com/baeldung/concurrent/lock/StripedLock.java new file mode 100644 index 0000000000..47c47d8813 --- /dev/null +++ b/core-java-modules/core-java-concurrency-collections-2/src/main/java/com/baeldung/concurrent/lock/StripedLock.java @@ -0,0 +1,41 @@ +package com.baeldung.concurrent.lock; + +import java.util.Map; +import java.util.concurrent.locks.Lock; + +import com.google.common.base.Supplier; +import com.google.common.util.concurrent.Striped; + +public class StripedLock extends ConcurrentAccessExperiment { + Striped stripedLock; + + public StripedLock(int buckets) { + stripedLock = Striped.lock(buckets); + } + + protected Supplier putSupplier(Map map, int key) { + return (()-> { + int bucket = key % stripedLock.size(); + Lock lock = stripedLock.get(bucket); + lock.lock(); + try { + return map.put("key" + key, "value" + key); + } finally { + lock.unlock(); + } + }); + } + + protected Supplier getSupplier(Map map, int key) { + return (()-> { + int bucket = key % stripedLock.size(); + Lock lock = stripedLock.get(bucket); + lock.lock(); + try { + return map.get("key" + key); + } finally { + lock.unlock(); + } + }); + } +}