Singleton
Why to use?
Enable handling global state force to use single instance.
When to use?
- Ensure just only one instance existence
- 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.
Bill Pugh Solution (Recommended)
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 ofobject
This is awesome solution.
- Thread-safe
- Avoid performance degradation
- Instance lazy loading
Instance is not loaded untilMySingleton.instance
is called.
📝 Conclusion
Use Bill Pugh solution when you should use singleton pattern if you don't need a constructor parameter passing.