Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package design.pattern.singleton;

import java.util.concurrent.atomic.AtomicLong;

public class DoubleCheckedLocking {

private AtomicLong id = new AtomicLong(0);
private static DoubleCheckedLocking instance;

private DoubleCheckedLocking() {
}

public static DoubleCheckedLocking getInstance() {
if (instance == null) {
synchronized (DoubleCheckedLocking.class) {
instance = new DoubleCheckedLocking();
}
}

return instance;
}

public long getId() {
return id.incrementAndGet();
}
}
20 changes: 20 additions & 0 deletions java/app/src/main/java/design/pattern/singleton/EagerLoading.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package design.pattern.singleton;

import java.util.concurrent.atomic.AtomicLong;

public class EagerLoading {

private AtomicLong id = new AtomicLong(0);
private static final EagerLoading instance = new EagerLoading();

private EagerLoading() {
}

public static EagerLoading getInstance() {
return instance;
}

public long getId() {
return id.incrementAndGet();
}
}
24 changes: 24 additions & 0 deletions java/app/src/main/java/design/pattern/singleton/HolderIdiom.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package design.pattern.singleton;

import java.util.concurrent.atomic.AtomicLong;

public class HolderIdiom {

private AtomicLong id = new AtomicLong(0);

private HolderIdiom() {
}

private static class SingletonHolderIdiom {

private static final HolderIdiom instance = new HolderIdiom();
}

public static HolderIdiom getInstance() {
return SingletonHolderIdiom.instance;
}

public long getId() {
return id.incrementAndGet();
}
}
23 changes: 23 additions & 0 deletions java/app/src/main/java/design/pattern/singleton/LazyLoading.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package design.pattern.singleton;

import java.util.concurrent.atomic.AtomicLong;

public class LazyLoading {

private AtomicLong id = new AtomicLong(0);
private static LazyLoading instance;

private LazyLoading() {
}

public static synchronized LazyLoading getInstance() {
if (instance == null) {
instance = new LazyLoading();
}
return instance;
}

public long getId() {
return id.incrementAndGet();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package design.pattern.singleton;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;

class DoubleCheckedLockingTest {

@Test
void sameInstance() {
DoubleCheckedLocking instance1 = DoubleCheckedLocking.getInstance();
DoubleCheckedLocking instance2 = DoubleCheckedLocking.getInstance();

assertThat(instance1).isSameAs(instance2);
}

@Test
void incrementId() {
DoubleCheckedLocking instance = DoubleCheckedLocking.getInstance();
long id1 = instance.getId();
long id2 = instance.getId();

assertThat(id1).isEqualTo(1L);
assertThat(id2).isEqualTo(2L);
}

@Test
void getId_threadSafe() throws InterruptedException {
int numberOfThreads = 1000;
ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads);

Set<Long> generatedIds = new HashSet<>();

for (int i = 0; i < numberOfThreads; i++) {
executor.execute(() -> {
long id = DoubleCheckedLocking.getInstance().getId();
synchronized (generatedIds) {
generatedIds.add(id);
}
});
}

executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);

assertThat(generatedIds.size()).isEqualTo(numberOfThreads);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package design.pattern.singleton;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

class EagerLoadingTest {

@Test
@DisplayName("두 객체가 동일 여부 확인")
void sameInstance() {
EagerLoading instance1 = EagerLoading.getInstance();
EagerLoading instance2 = EagerLoading.getInstance();

assertThat(instance1).isSameAs(instance2);
}

@Test
@DisplayName("객체가 생성 될 때마다 id가 증가")
void incrementAndGet() {
EagerLoading instance = EagerLoading.getInstance();

long id1 = instance.getId();
long id2 = instance.getId();

assertThat(id1).isEqualTo(1);
assertThat(id2).isEqualTo(2);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package design.pattern.singleton;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.Test;

class HolderIdiomTest {

@Test
void sameInstance() {
HolderIdiom instance1 = HolderIdiom.getInstance();
HolderIdiom instance2 = HolderIdiom.getInstance();

assertThat(instance1).isSameAs(instance2);
}

@Test
void incrementAndGet() {
HolderIdiom instance = HolderIdiom.getInstance();
long id1 = instance.getId();
long id2 = instance.getId();

assertThat(id1).isEqualTo(1);
assertThat(id2).isEqualTo(2);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package design.pattern.singleton;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;

class LazyLoadingTest {

@Test
void sameInstance() {
LazyLoading instance1 = LazyLoading.getInstance();
LazyLoading instance2 = LazyLoading.getInstance();

assertThat(instance1).isSameAs(instance2);
}

@Test
void incrementId() {
LazyLoading instance = LazyLoading.getInstance();
long id1 = instance.getId();
long id2 = instance.getId();

assertThat(id1).isEqualTo(1);
assertThat(id2).isEqualTo(2);
}

@Test
void incrementId_threadSage() throws InterruptedException {
int THREAD_SIZE = 1000;
ExecutorService executor = Executors.newFixedThreadPool(THREAD_SIZE);
Set<Long> generatedIds = new HashSet<>();

LazyLoading instance = LazyLoading.getInstance();

for (int i = 0; i < THREAD_SIZE; i++) {
executor.execute(() -> {
long id = instance.getId();
synchronized (generatedIds) {
generatedIds.add(id);
}
});
}

executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);

assertThat(generatedIds.size()).isEqualTo(THREAD_SIZE);
}
}