Skip to content

Commit 23ed71f

Browse files
authored
[BAEL-9257] Ways to Ensure Thread Safe Singleton Pattern in Java (#18659)
* [BAEL-9257] Add all the variations and unit tests * [BAEL-9257] Add all the variations and unit tests
1 parent 2aa6acc commit 23ed71f

File tree

11 files changed

+219
-0
lines changed

11 files changed

+219
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.baeldung.threadsafe;
2+
3+
public class DoubleCheckedSingleton {
4+
private static volatile DoubleCheckedSingleton instance;
5+
6+
private DoubleCheckedSingleton() {}
7+
8+
public static DoubleCheckedSingleton getInstance() {
9+
if (instance == null) {
10+
synchronized (DoubleCheckedSingleton.class) {
11+
if (instance == null) {
12+
instance = new DoubleCheckedSingleton();
13+
}
14+
}
15+
}
16+
return instance;
17+
}
18+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.baeldung.threadsafe;
2+
3+
public class EagerSingleton {
4+
private static final EagerSingleton INSTANCE = new EagerSingleton();
5+
6+
private EagerSingleton() {}
7+
8+
public static EagerSingleton getInstance() {
9+
return INSTANCE;
10+
}
11+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.baeldung.threadsafe;
2+
3+
public enum EnumSingleton {
4+
INSTANCE;
5+
6+
public void performOperation() {
7+
// Singleton operations here
8+
}
9+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.baeldung.threadsafe;
2+
3+
public class SimpleSingleton {
4+
private static SimpleSingleton instance;
5+
6+
private SimpleSingleton() {
7+
}
8+
9+
public static SimpleSingleton getInstance() {
10+
if (instance == null) {
11+
instance = new SimpleSingleton();
12+
}
13+
return instance;
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.baeldung.threadsafe;
2+
3+
public class SynchronizedSingleton {
4+
private static SynchronizedSingleton instance;
5+
6+
private SynchronizedSingleton() {}
7+
8+
public static synchronized SynchronizedSingleton getInstance() {
9+
if (instance == null) {
10+
instance = new SynchronizedSingleton();
11+
}
12+
return instance;
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.baeldung.threadsafe;
2+
3+
import com.baledung.billpugh.BillPughSingleton;
4+
import org.junit.jupiter.api.Test;
5+
import static org.junit.jupiter.api.Assertions.*;
6+
import java.util.concurrent.*;
7+
import java.util.Set;
8+
import java.util.HashSet;
9+
10+
public class BillPughSingletonUnitTest {
11+
@Test
12+
void testThreadSafety() throws InterruptedException {
13+
int numberOfThreads = 10;
14+
CountDownLatch latch = new CountDownLatch(numberOfThreads);
15+
Set<BillPughSingleton> instances = ConcurrentHashMap.newKeySet();
16+
17+
for (int i = 0; i < numberOfThreads; i++) {
18+
new Thread(() -> {
19+
instances.add(BillPughSingleton.getInstance());
20+
latch.countDown();
21+
}).start();
22+
}
23+
24+
latch.await(5, TimeUnit.SECONDS);
25+
26+
assertEquals(1, instances.size(), "All threads should get the same instance");
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.baeldung.threadsafe;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import java.util.ArrayList;
6+
import java.util.Collections;
7+
import java.util.HashSet;
8+
import java.util.List;
9+
import java.util.stream.IntStream;
10+
11+
import static org.junit.jupiter.api.Assertions.assertEquals;
12+
13+
public class DoubleCheckedSingletonUnitTest {
14+
@Test
15+
void givenDCLSingleton_whenAccessedFromThreads_thenOneInstanceCreated() {
16+
List<Object> instances = Collections.synchronizedList(new ArrayList<>());
17+
IntStream.range(0, 100).parallel().forEach(i -> instances.add(DoubleCheckedSingleton.getInstance()));
18+
assertEquals(1, new HashSet<>(instances).size());
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.baeldung.threadsafe;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import java.util.Set;
6+
import java.util.concurrent.ConcurrentHashMap;
7+
import java.util.concurrent.CountDownLatch;
8+
9+
import static org.junit.jupiter.api.Assertions.assertEquals;
10+
11+
public class EagerSingletonUnitTest {
12+
@Test
13+
void givenEagerSingleton_whenAccessedConcurrently_thenSingleInstanceCreated()
14+
throws InterruptedException {
15+
16+
int threadCount = 1000;
17+
Set<EagerSingleton> instances = ConcurrentHashMap.newKeySet();
18+
CountDownLatch latch = new CountDownLatch(threadCount);
19+
20+
for (int i = 0; i < threadCount; i++) {
21+
new Thread(() -> {
22+
instances.add(EagerSingleton.getInstance());
23+
latch.countDown();
24+
}).start();
25+
}
26+
27+
latch.await();
28+
29+
assertEquals(1, instances.size(), "Only one instance should be created");
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.baeldung.threadsafe;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import java.util.Set;
6+
import java.util.concurrent.ConcurrentHashMap;
7+
import java.util.concurrent.CountDownLatch;
8+
9+
import static org.junit.jupiter.api.Assertions.assertEquals;
10+
11+
public class EnumSingletonUnitTest {
12+
@Test
13+
void givenEnumSingleton_whenAccessedConcurrently_thenSingleInstanceCreated()
14+
throws InterruptedException {
15+
16+
Set<EnumSingleton> instances = ConcurrentHashMap.newKeySet();
17+
CountDownLatch latch = new CountDownLatch(100);
18+
19+
for (int i = 0; i < 100; i++) {
20+
new Thread(() -> {
21+
instances.add(EnumSingleton.INSTANCE);
22+
latch.countDown();
23+
}).start();
24+
}
25+
26+
latch.await();
27+
assertEquals(1, instances.size());
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.baeldung.threadsafe;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import java.util.Set;
6+
import java.util.concurrent.ConcurrentHashMap;
7+
import java.util.concurrent.CountDownLatch;
8+
9+
import static org.junit.jupiter.api.Assertions.assertTrue;
10+
11+
public class SimpleSingletonUnitTest {
12+
@Test
13+
void givenUnsafeSingleton_whenAccessedConcurrently_thenMultipleInstancesCreated() throws InterruptedException {
14+
int threadCount = 1000;
15+
Set<SimpleSingleton> instances = ConcurrentHashMap.newKeySet();
16+
CountDownLatch latch = new CountDownLatch(threadCount);
17+
for (int i = 0; i < threadCount; i++) {
18+
new Thread(() -> {
19+
instances.add(SimpleSingleton.getInstance());
20+
latch.countDown();
21+
}).start();
22+
}
23+
latch.await();
24+
assertTrue(instances.size() > 1, "Multiple instances were created");
25+
}
26+
}

0 commit comments

Comments
 (0)