diff --git a/patterns-modules/design-patterns-singleton/src/main/java/com/baeldung/threadsafe/DoubleCheckedSingleton.java b/patterns-modules/design-patterns-singleton/src/main/java/com/baeldung/threadsafe/DoubleCheckedSingleton.java new file mode 100644 index 000000000000..a0041fb68be6 --- /dev/null +++ b/patterns-modules/design-patterns-singleton/src/main/java/com/baeldung/threadsafe/DoubleCheckedSingleton.java @@ -0,0 +1,18 @@ +package com.baeldung.threadsafe; + +public class DoubleCheckedSingleton { + private static volatile DoubleCheckedSingleton instance; + + private DoubleCheckedSingleton() {} + + public static DoubleCheckedSingleton getInstance() { + if (instance == null) { + synchronized (DoubleCheckedSingleton.class) { + if (instance == null) { + instance = new DoubleCheckedSingleton(); + } + } + } + return instance; + } +} diff --git a/patterns-modules/design-patterns-singleton/src/main/java/com/baeldung/threadsafe/EagerSingleton.java b/patterns-modules/design-patterns-singleton/src/main/java/com/baeldung/threadsafe/EagerSingleton.java new file mode 100644 index 000000000000..57841dc016e4 --- /dev/null +++ b/patterns-modules/design-patterns-singleton/src/main/java/com/baeldung/threadsafe/EagerSingleton.java @@ -0,0 +1,11 @@ +package com.baeldung.threadsafe; + +public class EagerSingleton { + private static final EagerSingleton INSTANCE = new EagerSingleton(); + + private EagerSingleton() {} + + public static EagerSingleton getInstance() { + return INSTANCE; + } +} diff --git a/patterns-modules/design-patterns-singleton/src/main/java/com/baeldung/threadsafe/EnumSingleton.java b/patterns-modules/design-patterns-singleton/src/main/java/com/baeldung/threadsafe/EnumSingleton.java new file mode 100644 index 000000000000..60f8463be23e --- /dev/null +++ b/patterns-modules/design-patterns-singleton/src/main/java/com/baeldung/threadsafe/EnumSingleton.java @@ -0,0 +1,9 @@ +package com.baeldung.threadsafe; + +public enum EnumSingleton { + INSTANCE; + + public void performOperation() { + // Singleton operations here + } +} \ No newline at end of file diff --git a/patterns-modules/design-patterns-singleton/src/main/java/com/baeldung/threadsafe/SimpleSingleton.java b/patterns-modules/design-patterns-singleton/src/main/java/com/baeldung/threadsafe/SimpleSingleton.java new file mode 100644 index 000000000000..f0d853061c95 --- /dev/null +++ b/patterns-modules/design-patterns-singleton/src/main/java/com/baeldung/threadsafe/SimpleSingleton.java @@ -0,0 +1,15 @@ +package com.baeldung.threadsafe; + +public class SimpleSingleton { + private static SimpleSingleton instance; + + private SimpleSingleton() { + } + + public static SimpleSingleton getInstance() { + if (instance == null) { + instance = new SimpleSingleton(); + } + return instance; + } +} diff --git a/patterns-modules/design-patterns-singleton/src/main/java/com/baeldung/threadsafe/SynchronizedSingleton.java b/patterns-modules/design-patterns-singleton/src/main/java/com/baeldung/threadsafe/SynchronizedSingleton.java new file mode 100644 index 000000000000..c44dc78f8029 --- /dev/null +++ b/patterns-modules/design-patterns-singleton/src/main/java/com/baeldung/threadsafe/SynchronizedSingleton.java @@ -0,0 +1,14 @@ +package com.baeldung.threadsafe; + +public class SynchronizedSingleton { + private static SynchronizedSingleton instance; + + private SynchronizedSingleton() {} + + public static synchronized SynchronizedSingleton getInstance() { + if (instance == null) { + instance = new SynchronizedSingleton(); + } + return instance; + } +} diff --git a/patterns-modules/design-patterns-singleton/src/test/java/com/baeldung/threadsafe/BillPughSingletonUnitTest.java b/patterns-modules/design-patterns-singleton/src/test/java/com/baeldung/threadsafe/BillPughSingletonUnitTest.java new file mode 100644 index 000000000000..54cc2d7eefc8 --- /dev/null +++ b/patterns-modules/design-patterns-singleton/src/test/java/com/baeldung/threadsafe/BillPughSingletonUnitTest.java @@ -0,0 +1,28 @@ +package com.baeldung.threadsafe; + +import com.baledung.billpugh.BillPughSingleton; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; +import java.util.concurrent.*; +import java.util.Set; +import java.util.HashSet; + +public class BillPughSingletonUnitTest { + @Test + void testThreadSafety() throws InterruptedException { + int numberOfThreads = 10; + CountDownLatch latch = new CountDownLatch(numberOfThreads); + Set instances = ConcurrentHashMap.newKeySet(); + + for (int i = 0; i < numberOfThreads; i++) { + new Thread(() -> { + instances.add(BillPughSingleton.getInstance()); + latch.countDown(); + }).start(); + } + + latch.await(5, TimeUnit.SECONDS); + + assertEquals(1, instances.size(), "All threads should get the same instance"); + } +} \ No newline at end of file diff --git a/patterns-modules/design-patterns-singleton/src/test/java/com/baeldung/threadsafe/DoubleCheckedSingletonUnitTest.java b/patterns-modules/design-patterns-singleton/src/test/java/com/baeldung/threadsafe/DoubleCheckedSingletonUnitTest.java new file mode 100644 index 000000000000..69c9a4457e85 --- /dev/null +++ b/patterns-modules/design-patterns-singleton/src/test/java/com/baeldung/threadsafe/DoubleCheckedSingletonUnitTest.java @@ -0,0 +1,20 @@ +package com.baeldung.threadsafe; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.stream.IntStream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class DoubleCheckedSingletonUnitTest { + @Test + void givenDCLSingleton_whenAccessedFromThreads_thenOneInstanceCreated() { + List instances = Collections.synchronizedList(new ArrayList<>()); + IntStream.range(0, 100).parallel().forEach(i -> instances.add(DoubleCheckedSingleton.getInstance())); + assertEquals(1, new HashSet<>(instances).size()); + } +} diff --git a/patterns-modules/design-patterns-singleton/src/test/java/com/baeldung/threadsafe/EagerSingletonUnitTest.java b/patterns-modules/design-patterns-singleton/src/test/java/com/baeldung/threadsafe/EagerSingletonUnitTest.java new file mode 100644 index 000000000000..abbd5e21ac9e --- /dev/null +++ b/patterns-modules/design-patterns-singleton/src/test/java/com/baeldung/threadsafe/EagerSingletonUnitTest.java @@ -0,0 +1,31 @@ +package com.baeldung.threadsafe; + +import org.junit.jupiter.api.Test; + +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class EagerSingletonUnitTest { + @Test + void givenEagerSingleton_whenAccessedConcurrently_thenSingleInstanceCreated() + throws InterruptedException { + + int threadCount = 1000; + Set instances = ConcurrentHashMap.newKeySet(); + CountDownLatch latch = new CountDownLatch(threadCount); + + for (int i = 0; i < threadCount; i++) { + new Thread(() -> { + instances.add(EagerSingleton.getInstance()); + latch.countDown(); + }).start(); + } + + latch.await(); + + assertEquals(1, instances.size(), "Only one instance should be created"); + } +} diff --git a/patterns-modules/design-patterns-singleton/src/test/java/com/baeldung/threadsafe/EnumSingletonUnitTest.java b/patterns-modules/design-patterns-singleton/src/test/java/com/baeldung/threadsafe/EnumSingletonUnitTest.java new file mode 100644 index 000000000000..6ec6335d61d2 --- /dev/null +++ b/patterns-modules/design-patterns-singleton/src/test/java/com/baeldung/threadsafe/EnumSingletonUnitTest.java @@ -0,0 +1,29 @@ +package com.baeldung.threadsafe; + +import org.junit.jupiter.api.Test; + +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class EnumSingletonUnitTest { + @Test + void givenEnumSingleton_whenAccessedConcurrently_thenSingleInstanceCreated() + throws InterruptedException { + + Set instances = ConcurrentHashMap.newKeySet(); + CountDownLatch latch = new CountDownLatch(100); + + for (int i = 0; i < 100; i++) { + new Thread(() -> { + instances.add(EnumSingleton.INSTANCE); + latch.countDown(); + }).start(); + } + + latch.await(); + assertEquals(1, instances.size()); + } +} diff --git a/patterns-modules/design-patterns-singleton/src/test/java/com/baeldung/threadsafe/SimpleSingletonUnitTest.java b/patterns-modules/design-patterns-singleton/src/test/java/com/baeldung/threadsafe/SimpleSingletonUnitTest.java new file mode 100644 index 000000000000..50aedb8d427f --- /dev/null +++ b/patterns-modules/design-patterns-singleton/src/test/java/com/baeldung/threadsafe/SimpleSingletonUnitTest.java @@ -0,0 +1,26 @@ +package com.baeldung.threadsafe; + +import org.junit.jupiter.api.Test; + +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class SimpleSingletonUnitTest { + @Test + void givenUnsafeSingleton_whenAccessedConcurrently_thenMultipleInstancesCreated() throws InterruptedException { + int threadCount = 1000; + Set instances = ConcurrentHashMap.newKeySet(); + CountDownLatch latch = new CountDownLatch(threadCount); + for (int i = 0; i < threadCount; i++) { + new Thread(() -> { + instances.add(SimpleSingleton.getInstance()); + latch.countDown(); + }).start(); + } + latch.await(); + assertTrue(instances.size() > 1, "Multiple instances were created"); + } +} diff --git a/patterns-modules/design-patterns-singleton/src/test/java/com/baeldung/threadsafe/SynchronizedSingletonUnitTest.java b/patterns-modules/design-patterns-singleton/src/test/java/com/baeldung/threadsafe/SynchronizedSingletonUnitTest.java new file mode 100644 index 000000000000..73d381f67fab --- /dev/null +++ b/patterns-modules/design-patterns-singleton/src/test/java/com/baeldung/threadsafe/SynchronizedSingletonUnitTest.java @@ -0,0 +1,18 @@ +package com.baeldung.threadsafe; + +import org.junit.jupiter.api.Test; + +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.IntStream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class SynchronizedSingletonUnitTest { + @Test + void givenMultipleThreads_whenUsingSynchronizedSingleton_thenOnlyOneInstanceCreated() { + Set instances = ConcurrentHashMap.newKeySet(); + IntStream.range(0, 100).parallel().forEach(i -> instances.add(SynchronizedSingleton.getInstance())); + assertEquals(1, instances.size()); + } +}