diff --git a/core-java/src/main/java/com/baeldung/concurrent/atomic/SafeCounterWithLock.java b/core-java/src/main/java/com/baeldung/concurrent/atomic/SafeCounterWithLock.java new file mode 100644 index 0000000000..b2502018bb --- /dev/null +++ b/core-java/src/main/java/com/baeldung/concurrent/atomic/SafeCounterWithLock.java @@ -0,0 +1,13 @@ +package com.baeldung.concurrent.atomic; + +public class SafeCounterWithLock { + int counter; + + public int getValue() { + return counter; + } + + public synchronized void increment() { + counter++; + } +} diff --git a/core-java/src/main/java/com/baeldung/concurrent/atomic/SafeCounterWithoutLock.java b/core-java/src/main/java/com/baeldung/concurrent/atomic/SafeCounterWithoutLock.java new file mode 100644 index 0000000000..55226f4f13 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/concurrent/atomic/SafeCounterWithoutLock.java @@ -0,0 +1,21 @@ +package com.baeldung.concurrent.atomic; + +import java.util.concurrent.atomic.AtomicInteger; + +public class SafeCounterWithoutLock { + AtomicInteger counter = new AtomicInteger(0); + + public int getValue() { + return counter.get(); + } + + public void increment() { + while(true) { + int existingValue = getValue(); + int newValue = existingValue + 1; + if(counter.compareAndSet(existingValue, newValue)) { + return; + } + } + } +} diff --git a/core-java/src/main/java/com/baeldung/concurrent/atomic/UnsafeCounter.java b/core-java/src/main/java/com/baeldung/concurrent/atomic/UnsafeCounter.java new file mode 100644 index 0000000000..8a72788842 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/concurrent/atomic/UnsafeCounter.java @@ -0,0 +1,13 @@ +package com.baeldung.concurrent.atomic; + +public class UnsafeCounter { + int counter; + + public int getValue() { + return counter; + } + + public void increment() { + counter++; + } +} diff --git a/core-java/src/test/java/com/baeldung/concurrent/atomic/CounterTest.java b/core-java/src/test/java/com/baeldung/concurrent/atomic/CounterTest.java new file mode 100644 index 0000000000..1612c62513 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/concurrent/atomic/CounterTest.java @@ -0,0 +1,58 @@ +package com.baeldung.concurrent.atomic; + +import static org.junit.Assert.assertEquals; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; + +import org.junit.Ignore; +import org.junit.Test; + +public class CounterTest { + + /** + * This test shows the behaviour of a thread-unsafe class in a multithreaded scenario. We are calling + * the increment methods 1000 times from a pool of 3 threads. In most of the cases, the counter will + * less than 1000, because of lost updates, however, occasionally it may reach 1000, when no threads + * called the method simultaneously. This may cause the build to fail occasionally. Hence excluding this + * test by adding Ignore annotation. + */ + @Test + @Ignore + public void givenMultiThread_whenUnsafeCounterIncrement() throws InterruptedException { + ExecutorService service = Executors.newFixedThreadPool(3); + UnsafeCounter unsafeCounter = new UnsafeCounter(); + + IntStream.range(0, 1000) + .forEach(count -> service.submit(unsafeCounter::increment)); + service.awaitTermination(100, TimeUnit.MILLISECONDS); + + assertEquals(1000, unsafeCounter.getValue()); + } + + @Test + public void givenMultiThread_whenSafeCounterWithLockIncrement() throws InterruptedException { + ExecutorService service = Executors.newFixedThreadPool(3); + SafeCounterWithLock safeCounter = new SafeCounterWithLock(); + + IntStream.range(0, 1000) + .forEach(count -> service.submit(safeCounter::increment)); + service.awaitTermination(100, TimeUnit.MILLISECONDS); + + assertEquals(1000, safeCounter.getValue()); + } + + @Test + public void givenMultiThread_whenSafeCounterWithoutLockIncrement() throws InterruptedException { + ExecutorService service = Executors.newFixedThreadPool(3); + SafeCounterWithoutLock safeCounter = new SafeCounterWithoutLock(); + + IntStream.range(0, 1000) + .forEach(count -> service.submit(safeCounter::increment)); + service.awaitTermination(100, TimeUnit.MILLISECONDS); + + assertEquals(1000, safeCounter.getValue()); + } +}