From 34d04156fc8033181c42c956ff3ef148e8c9bc09 Mon Sep 17 00:00:00 2001 From: Unknown Date: Wed, 19 Feb 2020 23:14:00 +0100 Subject: [PATCH] BAEL-3855 Lock Striping --- lock-striping/pom.xml | 43 ++++++++++++ .../baeldung/concurrent/lock/BenchMark.java | 66 +++++++++++++++++++ .../concurrent/lock/CoarseGrained.java | 39 +++++++++++ .../concurrent/lock/ConcurrentAccessMap.java | 33 ++++++++++ .../baeldung/concurrent/lock/LockStriped.java | 47 +++++++++++++ .../com/baeldung/concurrent/lock/NoLock.java | 34 ++++++++++ 6 files changed, 262 insertions(+) create mode 100644 lock-striping/pom.xml create mode 100644 lock-striping/src/com/baeldung/concurrent/lock/BenchMark.java create mode 100644 lock-striping/src/com/baeldung/concurrent/lock/CoarseGrained.java create mode 100644 lock-striping/src/com/baeldung/concurrent/lock/ConcurrentAccessMap.java create mode 100644 lock-striping/src/com/baeldung/concurrent/lock/LockStriped.java create mode 100644 lock-striping/src/com/baeldung/concurrent/lock/NoLock.java diff --git a/lock-striping/pom.xml b/lock-striping/pom.xml new file mode 100644 index 0000000000..79469593cf --- /dev/null +++ b/lock-striping/pom.xml @@ -0,0 +1,43 @@ + + 4.0.0 + com.baeldung.concurrent.lock + lock-striping + 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/lock-striping/src/com/baeldung/concurrent/lock/BenchMark.java b/lock-striping/src/com/baeldung/concurrent/lock/BenchMark.java new file mode 100644 index 0000000000..5aae5145d2 --- /dev/null +++ b/lock-striping/src/com/baeldung/concurrent/lock/BenchMark.java @@ -0,0 +1,66 @@ +package com.baeldung.concurrent.lock; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +@State(Scope.Thread) +@Warmup(iterations = 2, time = 1, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 3, time = 1, timeUnit = TimeUnit.MILLISECONDS) +public class BenchMark { + ConcurrentAccessMap accessMyMap; + + @Param({"HashMap", "HashMap with Lock", "HashMap with Striped Lock", + "ConcurrentHashMap", "ConcurrentHashMap with Lock", "ConcurrentHashMap with Striped Lock"}) + private String type; + + @Setup + public void setup() { + switch (type) { + case "HashMap": + accessMyMap = new NoLock(getHashMap()); + break; + case "ConcurrentHashMap": + accessMyMap = new NoLock(getConcurrentHashMap()); + break; + case "HashMap with Lock": + accessMyMap = new CoarseGrained(getHashMap()); + break; + case "ConcurrentHashMap with Lock": + accessMyMap = new CoarseGrained(getConcurrentHashMap()); + break; + case "HashMap with Striped Lock": + accessMyMap = new LockStriped(getHashMap()); + break; + case "ConcurrentHashMap with Striped Lock": + accessMyMap = new LockStriped(getConcurrentHashMap()); + break; + } + } + + private Map getHashMap() { + return new HashMap(); + } + + private Map getConcurrentHashMap() { + return new ConcurrentHashMap(); + } + + @Benchmark + @BenchmarkMode(Mode.Throughput) + @OutputTimeUnit(TimeUnit.MILLISECONDS) + public void test() throws InterruptedException { + accessMyMap.doWork(type); + } +} diff --git a/lock-striping/src/com/baeldung/concurrent/lock/CoarseGrained.java b/lock-striping/src/com/baeldung/concurrent/lock/CoarseGrained.java new file mode 100644 index 0000000000..adc70fc3ab --- /dev/null +++ b/lock-striping/src/com/baeldung/concurrent/lock/CoarseGrained.java @@ -0,0 +1,39 @@ +package com.baeldung.concurrent.lock; + +import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; + +import com.google.common.base.Supplier; + +public class CoarseGrained extends ConcurrentAccessMap { + ReentrantLock lock; + + public CoarseGrained(Map map) { + super(map); + lock = new ReentrantLock(); + } + + protected Supplier putSupplier(int x) { + return (()-> { + boolean done = false; + while(!done) { + done = lock.tryLock(); + } + map.put("key" + x, "value" + x); + lock.unlock(); + return null; + }); + } + + protected Supplier getSupplier(int x) { + return (()-> { + boolean done = false; + while(!done) { + done = lock.tryLock(); + } + map.get("key" + x); + lock.unlock(); + return null; + }); + } +} diff --git a/lock-striping/src/com/baeldung/concurrent/lock/ConcurrentAccessMap.java b/lock-striping/src/com/baeldung/concurrent/lock/ConcurrentAccessMap.java new file mode 100644 index 0000000000..87c305b8aa --- /dev/null +++ b/lock-striping/src/com/baeldung/concurrent/lock/ConcurrentAccessMap.java @@ -0,0 +1,33 @@ +package com.baeldung.concurrent.lock; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import com.google.common.base.Supplier; + +public abstract class ConcurrentAccessMap { + static final int SLOTS = 4; + static final int THREADS = 10000; + static final int BUCKETS = Runtime.getRuntime().availableProcessors() * SLOTS; + private CompletableFuture[] requests; + Map map; + + public ConcurrentAccessMap(Map map) { + this.map = map; + } + + public final void doWork(String type) { + requests = new CompletableFuture[THREADS * SLOTS]; + + for (int i = 0; i < THREADS; i++) { + requests[SLOTS * i + 0] = CompletableFuture.supplyAsync(putSupplier(i)); + requests[SLOTS * i + 1] = CompletableFuture.supplyAsync(getSupplier(i)); + requests[SLOTS * i + 2] = CompletableFuture.supplyAsync(getSupplier(i)); + requests[SLOTS * i + 3] = CompletableFuture.supplyAsync(getSupplier(i)); + } + CompletableFuture.allOf(requests).join(); + } + + protected abstract Supplier putSupplier(int x); + protected abstract Supplier getSupplier(int x); +} diff --git a/lock-striping/src/com/baeldung/concurrent/lock/LockStriped.java b/lock-striping/src/com/baeldung/concurrent/lock/LockStriped.java new file mode 100644 index 0000000000..91d72eb362 --- /dev/null +++ b/lock-striping/src/com/baeldung/concurrent/lock/LockStriped.java @@ -0,0 +1,47 @@ +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 LockStriped extends ConcurrentAccessMap { + Striped lock; + + public LockStriped(Map map) { + super(map); + lock = getStripedLock(); + } + + private Striped getStripedLock() { + Striped map = Striped.lock(BUCKETS); + return map; + } + + protected Supplier putSupplier(int x) { + return (()-> { + Lock currentLock = lock.get("key" + x); + boolean done = false; + while(!done) { + done = currentLock.tryLock(); + } + map.put("key" + x, "value" + x); + currentLock.unlock(); + return null; + }); + } + + protected Supplier getSupplier(int x) { + return (()-> { + Lock currentLock = lock.get("key" + x); + boolean done = false; + while(!done) { + done = currentLock.tryLock(); + } + map.get("key" + x); + currentLock.unlock(); + return null; + }); + } +} diff --git a/lock-striping/src/com/baeldung/concurrent/lock/NoLock.java b/lock-striping/src/com/baeldung/concurrent/lock/NoLock.java new file mode 100644 index 0000000000..f2d5b445d7 --- /dev/null +++ b/lock-striping/src/com/baeldung/concurrent/lock/NoLock.java @@ -0,0 +1,34 @@ +package com.baeldung.concurrent.lock; + +import java.util.Map; + +import com.google.common.base.Supplier; + +public class NoLock extends ConcurrentAccessMap { + + public NoLock(Map map) { + super(map); + } + + protected Supplier putSupplier(int x) { + return (()-> { + boolean done = false; + while(!done) { + map.put("key" + x, "value" + x); + done = true; + } + return null; + }); + } + + protected Supplier getSupplier(int x) { + return (()-> { + boolean done = false; + while(!done) { + map.get("key" + x); + done = true; + } + return null; + }); + } +} \ No newline at end of file