Synchronized Java زبان میں ایک بلٹ ان سنکرونائزیشن میکانزم ہے جو کوڈ کے اہم حصوں تک خصوصی رسائی فراہم کرتا ہے۔ Oracle، 2024 کے مطابق، synchronized موڈیفائر اس بات کی ضمانت دیتا ہے کہ صرف ایک تھریڈ نشان زدہ میتھڈ یا بلاک کو ایک مخصوص وقت پر انجام دے سکتا ہے۔ یہ میکانزم مانیٹرز پر مبنی ہے — آپریٹنگ سسٹمز کا ایک بنیادی تصور جو تمام پیچیدگی کی سطحوں کی ملٹی تھریڈڈ ایپلی کیشنز کے درست آپریشن کو یقینی بناتا ہے۔
اہم نکات
Synchronized Java میں ایک کی ورڈ ہے جو اس بات کی ضمانت دیتا ہے کہ ایک وقت میں صرف ایک تھریڈ کوڈ کے محفوظ حصے کو انجام دیتا ہے، بیک وقت رسائی کے دوران ڈیٹا کی خرابی کو روکتا ہے۔ یہ Java کے پہلے ورژن میں ظاہر ہوا اور کسی بھی سطح کے ڈویلپرز کے لیے تھریڈ سیفٹی کو یقینی بنانے کا آسان ترین طریقہ ہے۔
synchronized موڈیفائر دو کام حل کرتا ہے: باہمی اخراج (mutual exclusion) اور تبدیلیوں کی مرئیت (visibility)۔ جب کوئی تھریڈ synchronized بلاک سے باہر نکلتا ہے، تو تمام تبدیلیاں دوسرے تھریڈز کو نظر آتی ہیں جو اسی آبجیکٹ پر سنکرونائزڈ بلاک میں داخل ہوتے ہیں۔
Synchronized کو پوری میتھڈ پر یا مانیٹر آبجیکٹ کی وضاحت کرتے ہوئے کسی بھی کوڈ بلاک پر لاگو کیا جا سکتا ہے۔ دونوں صورتوں میں، JVM بائٹ کوڈ کی سطح پر monitorenter اور monitorexit ہدایات داخل کرتا ہے۔
سنکرونائزیشن کے بغیر ملٹی تھریڈڈ ایپلی کیشنز میں، ریس کنڈیشن (race condition) پیدا ہوتی ہے — جب دو تھریڈ ایک ساتھ ایک ہی ڈیٹا کو تبدیل کرتے ہیں، جس کے نتیجے میں غیر متوقع نتائج نکلتے ہیں۔ Synchronized اس مسئلے سے نمٹنے کے لیے Java کا پہلا اور اہم ذریعہ بنا، جو کسی بھی ڈویلپر کے لیے قابل رسائی ایک سادہ اعلانیہ نحو فراہم کرتا ہے۔
Synchronized میکانزم مانیٹر کے تصور پر مبنی ہے — ایک اعلیٰ سطحی سنکرونائزیشن پریمیٹیو جو ہر Java آبجیکٹ میں بنایا گیا ہے۔ مانیٹر کسی آبجیکٹ سے اس وقت منسلک ہوتا ہے جب پہلی بار اس پر synchronized بلاک استعمال کیا جاتا ہے۔
Java میں ہر آبجیکٹ سے ایک منسلک مانیٹر ہوتا ہے۔ جب کوئی تھریڈ synchronized بلاک میں داخل ہوتا ہے، تو یہ آبجیکٹ کے مانیٹر کو حاصل کرتا ہے۔ اگر مانیٹر پہلے سے کسی دوسرے تھریڈ کے پاس ہے، تو تھریڈ اس وقت تک بلاک رہتا ہے جب تک یہ آزاد نہ ہو۔ بائٹ کوڈ میں، یہ monitorenter اور monitorexit ہدایات کے جوڑے سے مطابقت رکھتا ہے۔
JVM کئی سطحوں کے ذریعے synchronized کو بہتر بناتا ہے: سنگل تھریڈ رسائی کے لیے biased locking (مائل لاکنگ)، کم مسابقت کے لیے lightweight locking (ہلکی لاکنگ)، اور OS کی شرکت کے ساتھ شدید مسابقت کے لیے heavyweight locking (بھاری لاکنگ)۔ یہ سطحیں کوڈ کو تبدیل کیے بغیر کارکردگی کو بہتر کرتی ہیں۔
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
Synchronized ایک happens-before تعلق قائم کرتا ہے: synchronized بلاک سے باہر نکلنے سے پہلے ایک تھریڈ میں تمام اعمال دوسرے تھریڈ کو نظر آتے ہیں جب وہ اسی آبجیکٹ پر سنکرونائزڈ بلاک میں داخل ہوتا ہے۔ یہ نہ صرف باہمی اخراج بلکہ تمام تھریڈز کے لیے ڈیٹا کی مستقل مزاجی کو بھی یقینی بناتا ہے۔
Java synchronized کو لاگو کرنے کے دو طریقے فراہم کرتا ہے: میتھڈ کی سطح پر اور بلاک کی سطح پر۔ ان کے درمیان انتخاب کارکردگی اور سنکرونائزیشن کی دانے داری کو متاثر کرتا ہے۔
synchronized موڈیفائر کے ساتھ میتھڈ کو نشان زد کرنا اسے خود بخود موجودہ انسٹنس (عام میتھڈ کے لیے) یا Class آبجیکٹ (اسٹیٹک میتھڈ کے لیے) پر سنکرونائز کرتا ہے۔ یہ باہمی اخراج کو یقینی بنانے کا آسان ترین طریقہ ہے، لیکن یہ اکثر ضرورت سے زیادہ ہوتا ہے اگر اہم حصہ میتھڈ کا صرف ایک چھوٹا حصہ ہو اور باقی کوڈ کو سنکرونائزیشن کی ضرورت نہ ہو۔
Synchronized بلاک درست کنٹرول دیتا ہے: آپ مانیٹر آبجیکٹ کی وضاحت کرتے ہیں اور کوڈ کے صرف ضروری حصے کو سنکرونائز کرتے ہیں، باقی میتھڈ کو لاک سے باہر چھوڑتے ہیں۔ یہ مانیٹر کے قبضے کے وقت کو کم سے کم کرتا ہے اور ملٹی تھریڈڈ ماحول میں ایپلی کیشن کی مجموعی کارکردگی کو بہتر بناتا ہے، کیونکہ دوسرے تھریڈ مانیٹر کی آزادی کا انتظار کیے بغیر غیر متعلقہ کوڈ کو متوازی طور پر انجام دے سکتے ہیں۔
class DataProcessor {
private final Object lock = new Object();
public void process() {
// اہم حصے سے باہر کوڈ - سنکرونائزیشن کے بغیر
prepareData()
synchronized (lock) {
// صرف یہ بلاک محفوظ ہے
updateSharedState()
}
// لاک کے بغیر جاری
cleanup()
}
}
| معیار | Synchronized میتھڈ | Synchronized بلاک |
|---|---|---|
| مانیٹر | this (انسٹنس) یا Class | کوئی بھی آبجیکٹ |
| دانے داری | پوری میتھڈ | صرف ضروری کوڈ |
| پڑھنے کی اہلیت | اعلیٰ | درمیانی |
| کارکردگی | بڑی میتھڈز کے لیے کم | چھوٹے اہم حصوں کے لیے زیادہ |
Android ڈیولپمنٹ میں، synchronized SharedPreferences، ڈیٹا بیس تک رسائی اور UI اجزاء کی حفاظت کے لیے بڑے پیمانے پر استعمال ہوتا ہے۔ تاہم، انٹرفیس جم جانے کے خطرے کی وجہ سے مرکزی تھریڈ پر اس کا استعمال سختی سے منع کیا جاتا ہے۔
Android میں SharedPreferences بنیادی تھریڈ سیفٹی فراہم کرتا ہے، لیکن Editor کے ذریعے متعدد تھریڈز سے ترمیم کرتے وقت بیرونی سنکرونائزیشن کی ضرورت ہو سکتی ہے۔ علیحدہ لاک آبجیکٹ کے ساتھ synchronized بلاک تبدیلیوں کی مستقل مزاجی کو یقینی بناتا ہے۔
class PreferencesManager(private val prefs: SharedPreferences) {
private val lock = Any()
fun writeToken(token: String) {
synchronized (lock) {
prefs.edit()
.putString("auth_token", token)
.apply()
}
}
}
Android پر synchronized کی اہم حد تھریڈ بلاکنگ ہے۔ Mutex کے ساتھ coroutines کے برعکس، synchronized سسٹم تھریڈ کو مکمل طور پر بلاک کر دیتا ہے۔ مرکزی تھریڈ پر، یہ ANR کا سبب بنتا ہے۔ جدید Android ڈیولپمنٹ میں، synchronized کو coroutines (suspend Mutex) یا جوہری اقسام (AtomicInteger) سے تبدیل کرنے کی سفارش کی جاتی ہے۔
جدید Java اور Kotlin synchronized کے کئی متبادل فراہم کرتے ہیں، ہر ایک ایک ہی مسائل کو کم حدود یا بہتر کارکردگی کے ساتھ حل کرتا ہے۔
Lock انٹرفیس ReentrantLock اور ReadWriteLock کے نفاذ کے ساتھ ٹائم آؤٹ، قابل مداخلت انتظار اور متعدد Condition قطاریں فراہم کرتا ہے۔ یہ synchronized سے زیادہ لچکدار ہے لیکن finally میں واضح رہائی کی ضرورت ہے، جو unlock بھول جانے پر غلطی کا خطرہ بڑھاتا ہے۔
AtomicInteger، AtomicLong، AtomicReference اور دیگر کلاسیں CAS (Compare-And-Swap) پر مبنی Lock-Free الگورتھم استعمال کرتی ہیں۔ یہ معتدل مسابقت کے منظرناموں میں synchronized سے نمایاں طور پر تیز ہیں کیونکہ یہ تھریڈز کو بلاک نہیں کرتیں بلکہ OS کرنل سیاق و سباق کی تبدیلی کی ضرورت کے بغیر پرامید دوبارہ کوشش کرتی ہیں۔
ThreadLocal ایک متبادل نقطہ نظر فراہم کرتا ہے: ہر ThreadLocal متغیر ایک تھریڈ کے اندر الگ تھلگ ہوتا ہے اور پڑھنے اور لکھنے کے لیے سنکرونائزیشن کی ضرورت نہیں ہوتی۔ یہ ان ڈیٹا کے لیے synchronized کی ضرورت کو مکمل طور پر ختم کر دیتا ہے جو تھریڈز کے درمیان شیئر نہیں ہونا چاہیے۔ ThreadLocal فریم ورکس (Spring, Hibernate) میں ٹرانزیکشن سیاق و سباق اور سیشنز کو ذخیرہ کرنے کے لیے فعال طور پر استعمال ہوتا ہے۔
Android کے لیے Kotlin پروجیکٹس میں، synchronized کے متبادل کے طور پر kotlinx.coroutines سے Mutex ہے۔ یہ آپریٹنگ سسٹم کے تھریڈ کو بلاک نہیں کرتا بلکہ لاک کے آزاد ہونے تک coroutine کو معطل کر دیتا ہے — یہ پول تھریڈز کے موثر استعمال اور وسائل کی رہائی کے طویل انتظار کے دوران ANR سے بچنے کی اجازت دیتا ہے۔
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
val mutex = Mutex()
var counter = 0
suspend fun safeIncrement() {
mutex.withLock {
counter++
}
}
Synchronized کی کارکردگی حالیہ Java ورژنز میں نمایاں طور پر تبدیل ہوئی ہے۔ پہلے اسے ایک “بھاری” میکانزم سمجھا جاتا تھا، لیکن جدید JVMز نے جدید JIT کمپائلر آپٹیمائزیشنز کے ذریعے زیادہ تر اوور ہیڈ کو ختم کر دیا ہے۔ آئیے تفصیل سے دیکھتے ہیں کہ ورچوئل مشین رن ٹائم پر سنکرونائزڈ کوڈ کو کیسے تیز کرتی ہے۔
JVM کا JIT کمپائلر کئی آپٹیمائزیشنز کا اطلاق کرتا ہے: biased locking سنکرونائزیشن کو ختم کرتا ہے اگر لاک ہمیشہ اسی تھریڈ کے ذریعے حاصل کیا جاتا ہو؛ lock coarsening ملحقہ synchronized بلاکس کو ایک میں ملا دیتا ہے؛ lock elimination سنکرونائزیشن کو ہٹا دیتا ہے اگر آبجیکٹ صرف ایک تھریڈ کے لیے قابل رسائی ہو۔ یہ آپٹیمائزیشنز کم مسابقت پر synchronized کو عملی طور پر مفت بنا دیتی ہیں۔
JVM ہر آبجیکٹ کے لیے مسابقت کی سطح کا تعین کرتا ہے: جب کوئی مسابقت نہیں ہوتی، biased locking فعال ہو جاتا ہے؛ جب دوسرا تھریڈ ظاہر ہوتا ہے، لاک اسپن انتظار کے ساتھ ہلکے موڈ میں تبدیل ہو جاتا ہے؛ اور صرف طویل انتظار کے دوران یہ سسٹم میوٹیکس کے ساتھ بھاری موڈ میں تبدیل ہوتا ہے۔ یہ تبدیلی خود بخود ہوتی ہے، اور ڈویلپر کو دستی طور پر حکمت عملی منتخب کرنے کی ضرورت نہیں ہے۔
جدید بینچ مارکس (Java 17+) میں، synchronized کم اور معتدل مسابقت پر ReentrantLock کے برابر کارکردگی ظاہر کرتا ہے۔ زیادہ مسابقت پر، Lock کو ٹائم آؤٹ اور مداخلت کی حمایت کے ساتھ زیادہ موثر انتظار کی قطار کی بدولت فائدہ ہو سکتا ہے۔ زیادہ بوجھ والے نظاموں کے لیے جہاں مسابقت مستقل ہے، fair موڈ کے ساتھ ReentrantLock زیادہ پیش قیاسی رویہ فراہم کرتا ہے۔
جوہری کلاسز (AtomicInteger, AtomicReference) CAS پر مبنی Lock-Free نفاذ کی بدولت سادہ کاؤنٹرز اور جھنڈوں کے لیے تیز ترین رہتی ہیں۔ یہ تھریڈز کو بالکل بلاک نہیں کرتیں — تصادم پر، آپریشن بس ایک لوپ میں دوبارہ کوشش کرتا ہے۔ یہ 4-8 تھریڈز کے ساتھ کاؤنٹر بڑھانے کے آپریشنز پر synchronized کے مقابلے میں 3-5 گنا کارکردگی کا فائدہ دیتا ہے۔
اکثر پوچھے گئے سوالات
Synchronized باہمی اخراج اور مرئیت دونوں فراہم کرتا ہے۔ Volatile صرف تبدیلیوں کی مرئیت کی ضمانت دیتا ہے — volatile متغیر میں لکھنا تمام تھریڈز کو نظر آتا ہے لیکن بیک وقت تبدیلی کو نہیں روکتا، یعنی یہ ریس کنڈیشن سے تحفظ فراہم نہیں کرتا۔
ہاں، مختلف مانیٹر ترتیب کے ساتھ نیسٹڈ سنکرونائزیشن میں deadlock ممکن ہے۔ مثال کے طور پر، ایک تھریڈ synchronized(a) { synchronized(b) } کہتا ہے، جبکہ دوسرا synchronized(b) { synchronized(a) } کہتا ہے۔ نیسٹڈ synchronized بلاکس سے بچیں یا ایک مستقل مانیٹر ترتیب مقرر کریں۔
مانیٹر ایک سنکرونائزیشن میکانزم ہے جو ہر Java آبجیکٹ سے منسلک ہوتا ہے۔ یہ اس بات کی ضمانت دیتا ہے کہ صرف ایک تھریڈ اس آبجیکٹ پر synchronized کوڈ انجام دیتا ہے۔ مانیٹر میں ایک لاک، ایک انتظار کی قطار، اور wait/notify کے ذریعے اطلاع کے منتظر تھریڈز کا ایک پول شامل ہے۔
جدید Java ورژنز (17+) میں، synchronized JIT آپٹیمائزیشنز (biased locking, lock coarsening) کی بدولت کارکردگی میں Lock سے پیچھے نہیں ہے۔ Lock کو رفتار کے لیے نہیں بلکہ اضافی صلاحیتوں کے لیے ترجیح دی جاتی ہے: ٹائم آؤٹ، قابل مداخلت انتظار اور متعدد Condition قطاریں۔
اسٹیٹک synchronized میتھڈ انسٹنس کے بجائے دی گئی کلاس کے Class آبجیکٹ کے مانیٹر کا استعمال کرتی ہے۔ اس کا مطلب ہے کہ سنکرونائزیشن کلاس کی تمام انسٹنسز پر لاگو ہوتی ہے۔ غیر اسٹیٹک اور اسٹیٹک synchronized میتھڈز مختلف مانیٹر استعمال کرتی ہیں اور ایک دوسرے کو بلاک نہیں کرتیں۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں