Java Singleton Design Pattern: Thread-Safe ක්රම සහ Enum භාවිතය | SC Guide

කොහොමද ඉතින් මචන්ලා! අද අපි කතා කරන්න යන්නේ Java වල ගොඩක්ම වැදගත් Design Pattern එකක් ගැන, ඒ තමයි Singleton Design Pattern එක. මේක ගැන ඉගෙන ගන්න ඕනෙම Java Developer කෙනෙකුට මේ ලිපිය ගොඩක් වැදගත් වෙයි. විශේෂයෙන්ම, Multi-threading environments වලදී Singleton එකක් හරියට implement කරන්නේ කොහොමද කියලා අපි ගැඹුරින්ම බලමු. කට්ටියම ලෑස්තිද? එහෙනම් වැඩේට බහිමු!
Singleton කියන්නේ මොකක්ද?
සරලවම කිව්වොත්, Singleton Design Pattern එකෙන් කරන්නේ Class එකක තියෙන්නේ එකම Instance එකක් විතරයි කියලා sure කරන එකයි. ඒ Instance එකට Global Access Point එකක් තියෙනවා. හිතන්න, ඔයාගේ System එකේ Database Connection Pool එකක්, Logger එකක්, නැත්නම් Configuration Manager එකක් තියෙනවා කියලා. මේවට ඕන වෙන්නේ එක Instance එකක් විතරයි. නැත්නම් Resource wastage එකක් වෙන්න පුළුවන්, අනවශ්ය complications එන්න පුළුවන්. ඒ වගේ වෙලාවට Singleton Pattern එක සුපිරි විදිහට වැඩ කරනවා.
සාමාන්යයෙන් Singleton එකක් හදන්න මේ ප්රධාන කරුණු ටික තියෙන්න ඕන:
- Constructor එක Private කරන්න ඕනේ, ඒ නිසා Class එකට පිටින් Object හදන්න බෑ.
- Class එක ඇතුලෙම Private Static Instance එකක් තියාගන්න ඕනේ.
- Public Static Method එකක් (ගොඩක් වෙලාවට
getInstance()
) තියෙන්න ඕනේ, ඒ Method එකෙන් අර එකම Instance එක Return කරන්න.
Basic (Not Thread-Safe) Singleton Example
මෙන්න සරල Singleton එකක් හදන විදිහක්. මේක තවම Thread-Safe නෑ, හැබැයි මුලික අදහස ගන්න පුළුවන්:
public class BasicSingleton {
private static BasicSingleton instance;
private BasicSingleton() {
// Constructor is private to prevent external instantiation
}
public static BasicSingleton getInstance() {
if (instance == null) {
instance = new BasicSingleton();
}
return instance;
}
public void showMessage() {
System.out.println("Hello from Basic Singleton!");
}
}
මේක තනි Thread එකක වැඩ කරනකොට අවුලක් නෑ. හැබැයි Multiple Threads වලදී මේකේ අවුල් පටන් ගන්නවා. getInstance()
Method එකේ if (instance == null)
check එක Multiple Threads එකවර access කරනකොට Threads දෙක තුනකටම එකම වෙලාවේ instance = new BasicSingleton();
කියන line එක execute කරන්න පුළුවන්. එතකොට අපිට ලැබෙන්නේ එක Singleton Instance එකක් නෙවෙයි, Instance කිහිපයක්. මේක තමයි ප්රශ්නය!
Multi-threading වලදී එන ප්රශ්න සහ විසඳුම්
අපි දැක්කා, Thread-Safe නොවන Singleton එකක් Multi-threading environment එකකදී කොහොමද Fail වෙන්නේ කියලා. මේකෙන් වෙන්නේ Singleton Pattern එකේ මූලික අරමුණම නැති වෙන එක. මේ ප්රශ්නෙට විසඳුම් කිහිපයක් තියෙනවා, අපි ඒවයින් ගොඩක්ම ජනප්රිය සහ effective ක්රම දෙකක් ගැන අද කතා කරමු: Double-Checked Locking සහ Enum-based Singleton.
Thread-Safe Singleton ක්රම 1: Double-Checked Locking (DCL)
Double-Checked Locking (DCL) කියන්නේ Lazy Initialization එකත් එක්කම Thread-Safety දෙන්න පාවිච්චි කරන ටෙක්නික් එකක්. මේකෙන් කරන්නේ Method එකක් synchronize කරනවා වෙනුවට, Object එක හදන කොටස විතරක් synchronize කරන එක. මේකෙන් Performance එක ටිකක් වැඩි කරගන්න පුළුවන්, මොකද Synchronization Block එකට යන්නේ පළවෙනි වතාවට Instance එක හදනකොට විතරයි.
Double-Checked Locking වලදී volatile
Keyword එකේ වැදගත්කම
DCL වලදී instance
variable එක volatile
විදිහට declare කරන එක අනිවාර්යයි. ඇයි එහෙම කරන්නේ? මොකද, new BasicSingleton()
කියන operation එක එක පාරටම වෙන්නේ නෑ. Computer එක ඇතුලේ ඒක Steps තුනකට කැඩෙනවා:
- Memory එකක් Allocate කරනවා අලුත් Object එකට.
- Constructor එක Execute කරලා Object එක initialize කරනවා.
instance
variable එක, අර අලුතින් හදපු Object එකට Point කරනවා.
CPU එකේ Optimization නිසා මේ Steps වල Order එක මාරු වෙන්න පුළුවන්. ඒ කියන්නේ, instance
variable එක, Object එක සම්පූර්ණයෙන්ම initialize වෙන්න කලින්ම, ඒ Memory location එකට Point කරන්න පුළුවන් (Step 3, Step 2 ට කලින්). මේකට කියන්නේ Instruction Reordering කියලා.
volatile
Keyword එක දානකොට, මේ Instruction Reordering එක වළක්වනවා. ඒ කියන්නේ, Thread එකක් instance
කියන variable එක read කරනකොට, ඒක හරියට initialize වෙලා තියෙනවා කියලා sure කරනවා. එහෙම නැතුව volatile
නැතුව DCL එකක් ලිව්වොත්, Thread එකකට අඩක් හදපු Object එකක් ලැබෙන්න පුළුවන්. ඒකෙන් Broken Singleton එකක් හැදෙන්න පුළුවන්. මේක තමයි DCL වලදී volatile
ගේන පට්ටම වාසිය!
DCL Singleton Example
public class DCLSingleton {
// volatile ensures that changes to the instance variable are immediately visible
// to other threads and prevents instruction reordering.
private static volatile DCLSingleton instance;
private DCLSingleton() {
// Private constructor to prevent instantiation
}
public static DCLSingleton getInstance() {
if (instance == null) { // First check (no lock)
synchronized (DCLSingleton.class) { // Synchronize the block
if (instance == null) { // Second check (within lock)
instance = new DCLSingleton();
}
}
}
return instance;
}
public void showMessage() {
System.out.println("Hello from DCL Singleton!");
}
}
මේකේ, පළවෙනි if (instance == null)
check එකෙන් කරන්නේ, Instance එක තියෙනවනම්, Synchronization Block එකට යන්නේ නැතුවම Return කරන එක. Instance එක නැත්නම් විතරයි Synchronization Block එකට යන්නේ. ඒක ඇතුලේ තව පාරක් if (instance == null)
check කරනවා. මේකෙන් Instance එක එක පාරක් විතරක් හදනවා කියලා අනිවාර්යයෙන්ම sure කරනවා.
Thread-Safe Singleton ක්රම 2: Enum එකෙන් Singleton
Singleton එකක් හදන්න තියෙන හොඳම, Simpleම සහ Safeම ක්රමය තමයි Java Enum එකක් පාවිච්චි කරන එක. මේ ක්රමය Java 5 වලින් පස්සේ ආවේ. Effective Java by Joshua Bloch පොතේ මේ ක්රමය Best practice එකක් විදිහට recommend කරලා තියෙනවා. ඇයි එහෙම කියන්නේ?
- Thread-Safe By Default: Enum Types, JVM එකෙන්ම Thread-Safe විදිහට Handle කරනවා. ඒ නිසා අපිට වෙනම Synchronization Logic ලියන්න ඕනේ නෑ.
- Serialization Safety: සාමාන්ය Singleton එකක් Serialize කරලා De-serialize කරනකොට අලුත් Instance එකක් හැදෙන්න පුළුවන්. Enum Singleton එකේදී මේ ප්රශ්නය නෑ. JVM එක Enum එකක් Serialize/De-serialize කරන විදිහ Control කරනවා.
- Reflection Attack Protection: Reflection API එකෙන් Private Constructor එකට Access කරලා අලුත් Instance හදන්න පුළුවන්. Enum Constructor එකට එහෙම කරන්න බෑ. ඒ නිසා Enum-based Singleton එක Reflection Attacks වලින් ආරක්ෂිතයි.
- Simplicity: Code එක පට්ටම Simpleයි, කියවන්න ලේසියි.
Enum Singleton Example
public enum EnumSingleton {
// The single instance is declared as an enum constant
INSTANCE;
// You can add methods and fields as needed
public void showMessage() {
System.out.println("Hello from Enum Singleton!");
}
public String getConfig(String key) {
// Example: load configuration
return "Config value for " + key;
}
}
මේක පාවිච්චි කරන විදිහත් ගොඩක්ම ලේසියි:
public class SingletonUser {
public static void main(String[] args) {
// Get the single instance
EnumSingleton singleton = EnumSingleton.INSTANCE;
singleton.showMessage(); // Output: Hello from Enum Singleton!
// Another reference, still the same instance
EnumSingleton anotherSingleton = EnumSingleton.INSTANCE;
System.out.println(singleton == anotherSingleton); // Output: true
}
}
දැක්කා නේද කොච්චර ලේසිද කියලා? මට නම් මේ ක්රමය තමයි පට්ටම! ගොඩක් වෙලාවට මේක තමයි Production level applications වලදී recommend කරන්නේ.
අවසන් වශයෙන්
ඉතින් මචන්ලා, අද අපි Singleton Design Pattern එක ගැන හොඳටම කතා කළා. Thread-Safe නොවන සරලම ක්රමයේ ඉඳන්, Performance optimizations එක්ක එන Double-Checked Locking ක්රමය සහ Super Safe වගේම Simple Enum-based Singleton ක්රමය ගැනත් අපි කතා කළා. ඔයාලට දැන් තේරෙනවා ඇති, Project එකකදී කොයි ක්රමයද පාවිච්චි කරන්න ඕනේ කියලා. ගොඩක් වෙලාවට Enum-based Singleton එක තමයි හොඳම තේරීම, ඒකේ තියෙන Simplicity එකයි, inbuilt safety features නිසයි.
මේ ගැන ඔයාලට මොනව හරි ප්රශ්න තියෙනවා නම්, නැත්නම් ඔයාලා වෙනම Singleton Implement කරන විදිහක් දන්නවනම්, පහලින් Comment Section එකේ දාගෙන යන්න. ඒ වගේම මේ Article එක ඔයාලට වැදගත් වුණා නම් Share කරන්නත් අමතක කරන්න එපා! තවත් මේ වගේ පට්ට Article එකකින් හම්බෙමු! JAYAWEWA!