Skip to main content

Singleton

Why to use?

Enable handling global state force to use single instance.

When to use?

  1. Ensure just only one instance existence
  2. Handle global state

ex) Logging, Network state manager

How to use?

old java solution

This is not recommended because It's not ensuring single instance in multi-thread environment.

public class Singleton {
private static Singleton instance;
private Singleton() {}

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

// etc methods..
}

This code is not thread-safe.

Assume your environment is in dual thread env consists of Thread A, B
Thread B make new Singleton instance even though thread A already made one.

Simple kotlin solution

object

Use object for ensuring thread safe.

object Singleton {
// ...
}
object MySingleton {
val Priority = 3
fun myPriority() : String = "Hello"
}

class SingleTonPatternTest {
@Test
internal fun testMySingleTon() {
assertThat(MySingleton.Priority).isEqualTo(3)
assertThat(MySingleton.myPriority()).isEqualTo("Hello")
}
}

But object solution can't have parameter on constructor
If you want to pass parameter, Use companion object.

companion object

You can use companion object with parameter

class MySingleton private constructor(context: Context) {
companion object {
private var instance: MySingleton? = null

fun getInstance(context: Context) : MySingleton {
return instance ?: MySingleton(context).also {instance = it}
}
}
}

Not yet resolved multi-thread synchronization issue.

Double Check Locking

class MySingleton private constructor(context: Context) {
companion object {
@Volatile
private var instance: MySingleton? = null

fun getInstance(context: Context) : MySingleton {
// 1. null check
// 2. locking
instance ?: synchronized(MySingleton::class.java) {
instance ?: MySingleton(context).also { instance = it }
}
}
}
}

This solution ensures thread-safe

@Volatile enable each thread can access directly to main memory variable.
In this code, that's instance. However, this is possibility of performance degradation because of locking.

object MySingleton {
val instance: MySingleton by lazy {Holder.instance}
private object Holder {
val instance = MySingleton
}
}

BillPughSingletonTest.kt

    @Test
internal fun testBillPughSingletonTest() {
val firstInstance = MySingleton.instance
val secondInstance = MySingleton.instance
Assertions.assertThat(firstInstance).isEqualTo(secondInstance) // ✅
Assertions.assertThat(firstInstance).isSameAs(secondInstance) // ✅
}
Initialized!

In java, use static final keyword instead of object

This is awesome solution.

  1. Thread-safe
  2. Avoid performance degradation
  3. Instance lazy loading
    Instance is not loaded until MySingleton.instance is called.

📝 Conclusion

Use Bill Pugh solution when you should use singleton pattern if you don't need a constructor parameter passing.


🔗 Reference