diff --git a/core-java-modules/core-java-concurrency-basic/src/main/java/com/baeldung/concurrent/mutex/SequenceGenerator.java b/core-java-modules/core-java-concurrency-basic/src/main/java/com/baeldung/concurrent/mutex/SequenceGenerator.java new file mode 100644 index 0000000000..97d44e5156 --- /dev/null +++ b/core-java-modules/core-java-concurrency-basic/src/main/java/com/baeldung/concurrent/mutex/SequenceGenerator.java @@ -0,0 +1,11 @@ +package com.baeldung.concurrent.mutex; + +public class SequenceGenerator { + private int currentValue = 0; + + public int getNextSequence() throws InterruptedException { + currentValue = currentValue + 1; + Thread.sleep(500); + return currentValue; + } +} diff --git a/core-java-modules/core-java-concurrency-basic/src/main/java/com/baeldung/concurrent/mutex/SequenceGeneratorUsingMonitor.java b/core-java-modules/core-java-concurrency-basic/src/main/java/com/baeldung/concurrent/mutex/SequenceGeneratorUsingMonitor.java new file mode 100644 index 0000000000..30c8da4865 --- /dev/null +++ b/core-java-modules/core-java-concurrency-basic/src/main/java/com/baeldung/concurrent/mutex/SequenceGeneratorUsingMonitor.java @@ -0,0 +1,18 @@ +package com.baeldung.concurrent.mutex; + +import com.google.common.util.concurrent.Monitor; + +public class SequenceGeneratorUsingMonitor extends SequenceGenerator { + + private Monitor monitor = new Monitor(); + + @Override + public int getNextSequence() throws InterruptedException { + monitor.enter(); + try { + return super.getNextSequence(); + } finally { + monitor.leave(); + } + } +} diff --git a/core-java-modules/core-java-concurrency-basic/src/main/java/com/baeldung/concurrent/mutex/SequenceGeneratorUsingReentrantLock.java b/core-java-modules/core-java-concurrency-basic/src/main/java/com/baeldung/concurrent/mutex/SequenceGeneratorUsingReentrantLock.java new file mode 100644 index 0000000000..85ce780bda --- /dev/null +++ b/core-java-modules/core-java-concurrency-basic/src/main/java/com/baeldung/concurrent/mutex/SequenceGeneratorUsingReentrantLock.java @@ -0,0 +1,18 @@ +package com.baeldung.concurrent.mutex; + +import java.util.concurrent.locks.ReentrantLock; + +public class SequenceGeneratorUsingReentrantLock extends SequenceGenerator { + + private ReentrantLock mutex = new ReentrantLock(); + + @Override + public int getNextSequence() throws InterruptedException { + try { + mutex.lock(); + return super.getNextSequence(); + } finally { + mutex.unlock(); + } + } +} diff --git a/core-java-modules/core-java-concurrency-basic/src/main/java/com/baeldung/concurrent/mutex/SequenceGeneratorUsingSemaphore.java b/core-java-modules/core-java-concurrency-basic/src/main/java/com/baeldung/concurrent/mutex/SequenceGeneratorUsingSemaphore.java new file mode 100644 index 0000000000..fdece049e6 --- /dev/null +++ b/core-java-modules/core-java-concurrency-basic/src/main/java/com/baeldung/concurrent/mutex/SequenceGeneratorUsingSemaphore.java @@ -0,0 +1,18 @@ +package com.baeldung.concurrent.mutex; + +import java.util.concurrent.Semaphore; + +public class SequenceGeneratorUsingSemaphore extends SequenceGenerator { + + private Semaphore mutex = new Semaphore(1); + + @Override + public int getNextSequence() throws InterruptedException { + try { + mutex.acquire(); + return super.getNextSequence(); + } finally { + mutex.release(); + } + } +} diff --git a/core-java-modules/core-java-concurrency-basic/src/main/java/com/baeldung/concurrent/mutex/SequenceGeneratorUsingSynchronizedBlock.java b/core-java-modules/core-java-concurrency-basic/src/main/java/com/baeldung/concurrent/mutex/SequenceGeneratorUsingSynchronizedBlock.java new file mode 100644 index 0000000000..d485eae21c --- /dev/null +++ b/core-java-modules/core-java-concurrency-basic/src/main/java/com/baeldung/concurrent/mutex/SequenceGeneratorUsingSynchronizedBlock.java @@ -0,0 +1,12 @@ +package com.baeldung.concurrent.mutex; + +public class SequenceGeneratorUsingSynchronizedBlock extends SequenceGenerator { + + @Override + public int getNextSequence() throws InterruptedException { + synchronized (this) { + return super.getNextSequence(); + } + } + +} diff --git a/core-java-modules/core-java-concurrency-basic/src/main/java/com/baeldung/concurrent/mutex/SequenceGeneratorUsingSynchronizedMethod.java b/core-java-modules/core-java-concurrency-basic/src/main/java/com/baeldung/concurrent/mutex/SequenceGeneratorUsingSynchronizedMethod.java new file mode 100644 index 0000000000..441b33dc43 --- /dev/null +++ b/core-java-modules/core-java-concurrency-basic/src/main/java/com/baeldung/concurrent/mutex/SequenceGeneratorUsingSynchronizedMethod.java @@ -0,0 +1,10 @@ +package com.baeldung.concurrent.mutex; + +public class SequenceGeneratorUsingSynchronizedMethod extends SequenceGenerator { + + @Override + public synchronized int getNextSequence() throws InterruptedException { + return super.getNextSequence(); + } + +} diff --git a/core-java-modules/core-java-concurrency-basic/src/test/java/com/baeldung/concurrent/mutex/MutexUnitTest.java b/core-java-modules/core-java-concurrency-basic/src/test/java/com/baeldung/concurrent/mutex/MutexUnitTest.java new file mode 100644 index 0000000000..620179800a --- /dev/null +++ b/core-java-modules/core-java-concurrency-basic/src/test/java/com/baeldung/concurrent/mutex/MutexUnitTest.java @@ -0,0 +1,81 @@ +package com.baeldung.concurrent.mutex; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import org.junit.Assert; +import org.junit.Test; + +import com.baeldung.concurrent.mutex.SequenceGenerator; +import com.baeldung.concurrent.mutex.SequenceGeneratorUsingMonitor; +import com.baeldung.concurrent.mutex.SequenceGeneratorUsingReentrantLock; +import com.baeldung.concurrent.mutex.SequenceGeneratorUsingSemaphore; +import com.baeldung.concurrent.mutex.SequenceGeneratorUsingSynchronizedBlock; +import com.baeldung.concurrent.mutex.SequenceGeneratorUsingSynchronizedMethod; + +public class MutexUnitTest { + + private final int RANGE = 30; + + @Test + public void givenUnsafeSequenceGenerator_whenRaceCondition_thenUnexpectedBehavior() throws Exception { + Set uniqueSequences = getASetOFUniqueSequences(new SequenceGenerator()); + Assert.assertNotEquals(RANGE, uniqueSequences.size()); + } + + @Test + public void givenSequenceGeneratorUsingSynchronizedMethod_whenRaceCondition_thenSuccess() throws Exception { + Set uniqueSequences = getASetOFUniqueSequences(new SequenceGeneratorUsingSynchronizedMethod()); + Assert.assertEquals(RANGE, uniqueSequences.size()); + } + + @Test + public void givenSequenceGeneratorUsingSynchronizedBlock_whenRaceCondition_thenSuccess() throws Exception { + Set uniqueSequences = getASetOFUniqueSequences(new SequenceGeneratorUsingSynchronizedBlock()); + Assert.assertEquals(RANGE, uniqueSequences.size()); + } + + @Test + public void givenSequenceGeneratorUsingReentrantLock_whenRaceCondition_thenSuccess() throws Exception { + Set uniqueSequences = getASetOFUniqueSequences(new SequenceGeneratorUsingReentrantLock()); + Assert.assertEquals(RANGE, uniqueSequences.size()); + } + + @Test + public void givenSequenceGeneratorUsingSemaphore_whenRaceCondition_thenSuccess() throws Exception { + Set uniqueSequences = getASetOFUniqueSequences(new SequenceGeneratorUsingSemaphore()); + Assert.assertEquals(RANGE, uniqueSequences.size()); + } + + @Test + public void givenSequenceGeneratorUsingMonitor_whenRaceCondition_thenSuccess() throws Exception { + Set uniqueSequences = getASetOFUniqueSequences(new SequenceGeneratorUsingMonitor()); + Assert.assertEquals(RANGE, uniqueSequences.size()); + } + + private Set getASetOFUniqueSequences(SequenceGenerator generator) throws Exception { + ExecutorService executor = Executors.newFixedThreadPool(3); + Set uniqueSequences = new HashSet<>(); + List> futures = new ArrayList<>(); + + for (int i = 0; i < RANGE; i++) { + futures.add(executor.submit(generator::getNextSequence)); + } + + for (Future future : futures) { + uniqueSequences.add(future.get()); + } + + executor.awaitTermination(15, TimeUnit.SECONDS); + executor.shutdown(); + + return uniqueSequences; + } + +}