Skip to content
Merged
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
52 changes: 26 additions & 26 deletions singleton/src/main/java/com/iluwatar/singleton/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,40 +27,40 @@
import org.slf4j.LoggerFactory;

/**
* Singleton pattern ensures that the class can have only one existing instance per Java classloader
* instance and provides global access to it.
* <p>
* One of the risks of this pattern is that bugs resulting from setting a singleton up in a
* <p>Singleton pattern ensures that the class can have only one existing instance per Java
* classloader instance and provides global access to it.</p>
*
* <p>One of the risks of this pattern is that bugs resulting from setting a singleton up in a
* distributed environment can be tricky to debug, since it will work fine if you debug with a
* single classloader. Additionally, these problems can crop up a while after the implementation of
* a singleton, since they may start out synchronous and only become async with time, so you it may
* not be clear why you are seeing certain changes in behaviour.
* <p>
* There are many ways to implement the Singleton. The first one is the eagerly initialized instance
* in {@link IvoryTower}. Eager initialization implies that the implementation is thread safe. If
* you can afford giving up control of the instantiation moment, then this implementation will suit
* you fine.
* <p>
* The other option to implement eagerly initialized Singleton is enum based Singleton. The example
* is found in {@link EnumIvoryTower}. At first glance the code looks short and simple. However, you
* should be aware of the downsides including committing to implementation strategy, extending the
* enum class, serializability and restrictions to coding. These are extensively discussed in Stack
* Overflow:
* not be clear why you are seeing certain changes in behaviour.</p>
*
* <p>There are many ways to implement the Singleton. The first one is the eagerly initialized
* instance in {@link IvoryTower}. Eager initialization implies that the implementation is thread
* safe. If you can afford giving up control of the instantiation moment, then this implementation
* will suit you fine.</p>
*
* <p>The other option to implement eagerly initialized Singleton is enum based Singleton. The
* example is found in {@link EnumIvoryTower}. At first glance the code looks short and simple.
* However, you should be aware of the downsides including committing to implementation strategy,
* extending the enum class, serializability and restrictions to coding. These are extensively
* discussed in Stack Overflow:
* http://programmers.stackexchange.com/questions/179386/what-are-the-downsides-of-implementing
* -a-singleton-with-javas-enum
* <p>
* {@link ThreadSafeLazyLoadedIvoryTower} is a Singleton implementation that is initialized on
* -a-singleton-with-javas-enum</p>
*
* <p>{@link ThreadSafeLazyLoadedIvoryTower} is a Singleton implementation that is initialized on
* demand. The downside is that it is very slow to access since the whole access method is
* synchronized.
* <p>
* Another Singleton implementation that is initialized on demand is found in
* synchronized.</p>
*
* <p>Another Singleton implementation that is initialized on demand is found in
* {@link ThreadSafeDoubleCheckLocking}. It is somewhat faster than
* {@link ThreadSafeLazyLoadedIvoryTower} since it doesn't synchronize the whole access method but
* only the method internals on specific conditions.
* <p>
* Yet another way to implement thread safe lazily initialized Singleton can be found in
* only the method internals on specific conditions.</p>
*
* <p>Yet another way to implement thread safe lazily initialized Singleton can be found in
* {@link InitializingOnDemandHolderIdiom}. However, this implementation requires at least Java 8
* API level to work.
* API level to work.</p>
*/
public class App {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@
package com.iluwatar.singleton;

/**
* Enum based singleton implementation. Effective Java 2nd Edition (Joshua Bloch) p. 18
* <p>Enum based singleton implementation. Effective Java 2nd Edition (Joshua Bloch) p. 18</p>
*
* This implementation is thread safe, however adding any other method and its thread safety
* is developers responsibility.
* <p>This implementation is thread safe, however adding any other method and its thread safety
* is developers responsibility.</p>
*/
public enum EnumIvoryTower {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,16 @@
package com.iluwatar.singleton;

/**
* The Initialize-on-demand-holder idiom is a secure way of creating a lazy initialized singleton
* object in Java.
* <p>
* The technique is as lazy as possible and works in all known versions of Java. It takes advantage
* of language guarantees about class initialization, and will therefore work correctly in all
* Java-compliant compilers and virtual machines.
* <p>
* The inner class is referenced no earlier (and therefore loaded no earlier by the class loader) than
* the moment that getInstance() is called. Thus, this solution is thread-safe without requiring special
* language constructs (i.e. volatile or synchronized).
* <p>The Initialize-on-demand-holder idiom is a secure way of creating a lazy initialized singleton
* object in Java.</p>
*
* <p>The technique is as lazy as possible and works in all known versions of Java. It takes
* advantage of language guarantees about class initialization, and will therefore work correctly
* in all Java-compliant compilers and virtual machines.</p>
*
* <p>The inner class is referenced no earlier (and therefore loaded no earlier by the class loader)
* than the moment that getInstance() is called. Thus, this solution is thread-safe without
* requiring special language constructs (i.e. volatile or synchronized).</p>
*
*/
public final class InitializingOnDemandHolderIdiom {
Expand All @@ -44,6 +44,8 @@ public final class InitializingOnDemandHolderIdiom {
private InitializingOnDemandHolderIdiom() {}

/**
* Sigleton instance.
*
* @return Singleton instance
*/
public static InitializingOnDemandHolderIdiom getInstance() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@
package com.iluwatar.singleton;

/**
* Double check locking
* <p>
* http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
* <p>
* Broken under Java 1.4.
* <p>Double check locking.</p>
*
* <p>http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html</p>
*
* <p>Broken under Java 1.4.</p>
*
* @author mortezaadi@gmail.com
*/
Expand Down Expand Up @@ -60,17 +60,21 @@ public static ThreadSafeDoubleCheckLocking getInstance() {
// Joshua Bloch "Effective Java, Second Edition", p. 283-284

var result = instance;
// Check if singleton instance is initialized. If it is initialized then we can return the instance.
// Check if singleton instance is initialized.
// If it is initialized then we can return the instance.
if (result == null) {
// It is not initialized but we cannot be sure because some other thread might have initialized it
// in the meanwhile. So to make sure we need to lock on an object to get mutual exclusion.
// It is not initialized but we cannot be sure because some other thread might have
// initialized it in the meanwhile.
// So to make sure we need to lock on an object to get mutual exclusion.
synchronized (ThreadSafeDoubleCheckLocking.class) {
// Again assign the instance to local variable to check if it was initialized by some other thread
// while current thread was blocked to enter the locked zone. If it was initialized then we can
// return the previously created instance just like the previous null check.
// Again assign the instance to local variable to check if it was initialized by some
// other thread while current thread was blocked to enter the locked zone.
// If it was initialized then we can return the previously created instance
// just like the previous null check.
result = instance;
if (result == null) {
// The instance is still not initialized so we can safely (no other thread can enter this zone)
// The instance is still not initialized so we can safely
// (no other thread can enter this zone)
// create an instance and make it our singleton instance.
instance = result = new ThreadSafeDoubleCheckLocking();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@
package com.iluwatar.singleton;

/**
* Thread-safe Singleton class. The instance is lazily initialized and thus needs synchronization
* mechanism.
* <p>Thread-safe Singleton class. The instance is lazily initialized and thus needs synchronization
* mechanism.</p>
*
* Note: if created by reflection then a singleton will not be created but multiple options in the
* same classloader
* <p>Note: if created by reflection then a singleton will not be created but multiple options
* in the same classloader</p>
*/
public final class ThreadSafeLazyLoadedIvoryTower {

Expand Down
4 changes: 1 addition & 3 deletions singleton/src/test/java/com/iluwatar/singleton/AppTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@
import org.junit.jupiter.api.Test;

/**
*
* Application test
*
* Application test.
*/
public class AppTest {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@
package com.iluwatar.singleton;

/**
* Date: 12/29/15 - 19:20 PM
* Date: 12/29/15 - 19:20 PM.
*
* @author Jeroen Meulemeester
*/
public class EnumIvoryTowerTest extends SingletonTest<EnumIvoryTower> {

/**
* Create a new singleton test instance using the given 'getInstance' method
* Create a new singleton test instance using the given 'getInstance' method.
*/
public EnumIvoryTowerTest() {
super(() -> EnumIvoryTower.INSTANCE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@
package com.iluwatar.singleton;

/**
* Date: 12/29/15 - 19:22 PM
* Date: 12/29/15 - 19:22 PM.
*
* @author Jeroen Meulemeester
*/
public class InitializingOnDemandHolderIdiomTest extends SingletonTest<InitializingOnDemandHolderIdiom> {
public class InitializingOnDemandHolderIdiomTest
extends SingletonTest<InitializingOnDemandHolderIdiom> {

/**
* Create a new singleton test instance using the given 'getInstance' method
* Create a new singleton test instance using the given 'getInstance' method.
*/
public InitializingOnDemandHolderIdiomTest() {
super(InitializingOnDemandHolderIdiom::getInstance);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@
package com.iluwatar.singleton;

/**
* Date: 12/29/15 - 19:23 PM
* Date: 12/29/15 - 19:23 PM.
*
* @author Jeroen Meulemeester
*/
public class IvoryTowerTest extends SingletonTest<IvoryTower> {

/**
* Create a new singleton test instance using the given 'getInstance' method
* Create a new singleton test instance using the given 'getInstance' method.
*/
public IvoryTowerTest() {
super(IvoryTower::getInstance);
Expand Down
30 changes: 15 additions & 15 deletions singleton/src/test/java/com/iluwatar/singleton/SingletonTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@

package com.iluwatar.singleton;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import static java.time.Duration.ofMillis;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTimeout;

import java.util.ArrayList;
import java.util.List;
Expand All @@ -34,32 +36,30 @@
import java.util.concurrent.Future;
import java.util.function.Supplier;

import static java.time.Duration.ofMillis;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTimeout;
import org.junit.jupiter.api.Test;

/**
* This class provides several test case that test singleton construction.
* <p>This class provides several test case that test singleton construction.</p>
*
* <p>The first proves that multiple calls to the singleton getInstance object are the same when
* called in the SAME thread. The second proves that multiple calls to the singleton getInstance
* object are the same when called in the DIFFERENT thread.</p>
*
* The first proves that multiple calls to the singleton getInstance object are the same when called
* in the SAME thread. The second proves that multiple calls to the singleton getInstance object are
* the same when called in the DIFFERENT thread.
* <p>Date: 12/29/15 - 19:25 PM</p>
*
* Date: 12/29/15 - 19:25 PM
* @param <S> Supplier method generating singletons
* @author Jeroen Meulemeester
* @author Richard Jones
*/
public abstract class SingletonTest<S> {

/**
* The singleton's getInstance method
* The singleton's getInstance method.
*/
private final Supplier<S> singletonInstanceMethod;

/**
* Create a new singleton test instance using the given 'getInstance' method
* Create a new singleton test instance using the given 'getInstance' method.
*
* @param singletonInstanceMethod The singleton's getInstance method
*/
Expand All @@ -68,7 +68,7 @@ public SingletonTest(final Supplier<S> singletonInstanceMethod) {
}

/**
* Test the singleton in a non-concurrent setting
* Test the singleton in a non-concurrent setting.
*/
@Test
public void testMultipleCallsReturnTheSameObjectInSameThread() {
Expand All @@ -83,7 +83,7 @@ public void testMultipleCallsReturnTheSameObjectInSameThread() {
}

/**
* Test singleton instance in a concurrent setting
* Test singleton instance in a concurrent setting.
*/
@Test
public void testMultipleCallsReturnTheSameObjectInDifferentThreads() throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,34 +23,35 @@

package com.iluwatar.singleton;

import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

import org.junit.Test;

/**
* Date: 12/29/15 - 19:26 PM
* Date: 12/29/15 - 19:26 PM.
*
* @author Jeroen Meulemeester
*/
public class ThreadSafeDoubleCheckLockingTest extends SingletonTest<ThreadSafeDoubleCheckLocking> {

/**
* Create a new singleton test instance using the given 'getInstance' method
* Create a new singleton test instance using the given 'getInstance' method.
*/
public ThreadSafeDoubleCheckLockingTest() {
super(ThreadSafeDoubleCheckLocking::getInstance);
}

/**
* Test creating new instance by refection
* Test creating new instance by refection.
*/
@Test(expected = InvocationTargetException.class)
public void testCreatingNewInstanceByRefection() throws Exception {
ThreadSafeDoubleCheckLocking instance1 = ThreadSafeDoubleCheckLocking.getInstance();
Constructor constructor = ThreadSafeDoubleCheckLocking.class.getDeclaredConstructor();
constructor.setAccessible(true);
ThreadSafeDoubleCheckLocking instance2 = (ThreadSafeDoubleCheckLocking) constructor.newInstance(null);
ThreadSafeDoubleCheckLocking instance2 =
(ThreadSafeDoubleCheckLocking) constructor.newInstance(null);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@
package com.iluwatar.singleton;

/**
* Date: 12/29/15 - 19:26 PM
* Date: 12/29/15 - 19:26 PM.
*
* @author Jeroen Meulemeester
*/
public class ThreadSafeLazyLoadedIvoryTowerTest extends SingletonTest<ThreadSafeLazyLoadedIvoryTower> {
public class ThreadSafeLazyLoadedIvoryTowerTest
extends SingletonTest<ThreadSafeLazyLoadedIvoryTower> {

/**
* Create a new singleton test instance using the given 'getInstance' method
* Create a new singleton test instance using the given 'getInstance' method.
*/
public ThreadSafeLazyLoadedIvoryTowerTest() {
super(ThreadSafeLazyLoadedIvoryTower::getInstance);
Expand Down
Loading