CodeGym /جاوا بلاگ /Random-UR /ایک ساتھ بہتر: جاوا اور تھریڈ کلاس۔ حصہ II - ہم آہنگی
John Squirrels
سطح
San Francisco

ایک ساتھ بہتر: جاوا اور تھریڈ کلاس۔ حصہ II - ہم آہنگی

گروپ میں شائع ہوا۔

تعارف

تو، ہم جانتے ہیں کہ جاوا میں تھریڈز ہیں۔ آپ اس کے بارے میں بیٹر اکٹھے: جاوا اور تھریڈ کلاس کے عنوان سے جائزے میں پڑھ سکتے ہیں ۔ حصہ I - عمل درآمد کے سلسلے ۔ متوازی طور پر کام کرنے کے لیے تھریڈز ضروری ہیں۔ اس سے اس بات کا بہت زیادہ امکان ہوتا ہے کہ تھریڈز کسی نہ کسی طرح ایک دوسرے کے ساتھ تعامل کریں گے۔ آئیے دیکھتے ہیں کہ یہ کیسے ہوتا ہے اور ہمارے پاس کون سے بنیادی اوزار ہیں۔ ایک ساتھ بہتر: جاوا اور تھریڈ کلاس۔  حصہ II - ہم آہنگی - 1

پیداوار

Thread.yield() حیران کن ہے اور شاذ و نادر ہی استعمال ہوتا ہے۔ انٹرنیٹ پر اسے بہت سے مختلف طریقوں سے بیان کیا گیا ہے۔ بشمول کچھ لوگ لکھ رہے ہیں کہ دھاگوں کی کچھ قطار ہے، جس میں دھاگے کی ترجیحات کی بنیاد پر ایک دھاگہ اترے گا۔ دوسرے لوگ لکھتے ہیں کہ ایک تھریڈ اپنی حیثیت "رننگ" سے "رن ایبل" میں بدل دے گا (حالانکہ ان سٹیٹس میں کوئی فرق نہیں ہے، یعنی جاوا ان میں فرق نہیں کرتا)۔ حقیقت یہ ہے کہ یہ سب بہت کم معروف اور پھر بھی ایک لحاظ سے آسان ہے۔ ایک ساتھ بہتر: جاوا اور تھریڈ کلاس۔  حصہ II - ہم آہنگی - 2ایک بگ ہے ( JDK-6416721: (spec thread) Fix Thread.yield() javadoc ) yield()طریقہ کی دستاویزات کے لیے لاگ ان ہے۔ اگر آپ اسے پڑھتے ہیں، تو یہ واضح ہے کہ یہ yield()طریقہ دراصل جاوا تھریڈ شیڈیولر کو صرف کچھ سفارشات فراہم کرتا ہے کہ اس تھریڈ کو عملدرآمد کا کم وقت دیا جا سکتا ہے۔ لیکن اصل میں کیا ہوتا ہے، یعنی آیا شیڈولر سفارش پر عمل کرتا ہے اور عام طور پر کیا کرتا ہے، یہ JVM کے نفاذ اور آپریٹنگ سسٹم پر منحصر ہے۔ اور یہ کچھ دوسرے عوامل پر بھی منحصر ہوسکتا ہے۔ تمام الجھن غالباً اس حقیقت کی وجہ سے ہے کہ جاوا زبان کی ترقی کے ساتھ ہی ملٹی تھریڈنگ پر دوبارہ غور کیا گیا ہے۔ جائزہ میں یہاں مزید پڑھیں: Java Thread.yield() کا مختصر تعارف ۔

سونا

ایک دھاگہ اس کے عمل کے دوران سو سکتا ہے۔ یہ دوسرے دھاگوں کے ساتھ تعامل کی سب سے آسان قسم ہے۔ آپریٹنگ سسٹم جو جاوا ورچوئل مشین چلاتا ہے جو ہمارے جاوا کوڈ کو چلاتا ہے اس کا اپنا تھریڈ شیڈیولر ہے ۔ یہ فیصلہ کرتا ہے کہ کون سا تھریڈ کب شروع کرنا ہے۔ ایک پروگرامر اس شیڈیولر کے ساتھ جاوا کوڈ سے براہ راست بات چیت نہیں کرسکتا، صرف JVM کے ذریعے۔ وہ شیڈیولر سے تھریڈ کو تھوڑی دیر کے لیے روکنے کے لیے کہہ سکتا ہے، یعنی اسے سونے کے لیے۔ آپ ان مضامین میں مزید پڑھ سکتے ہیں: Thread.sleep() اور ملٹی تھریڈنگ کیسے کام کرتی ہے ۔ آپ یہ بھی دیکھ سکتے ہیں کہ ونڈوز آپریٹنگ سسٹم میں تھریڈز کیسے کام کرتے ہیں: Windows Thread کے اندرونی ۔ اور اب ہم اسے اپنی آنکھوں سے دیکھتے ہیں۔ درج ذیل کوڈ نام کی فائل میں محفوظ کریں HelloWorldApp.java:
class HelloWorldApp {
    public static void main(String []args) {
        Runnable task = () -> {
            try {
                int secToWait = 1000 * 60;
                Thread.currentThread().sleep(secToWait);
                System.out.println("Woke up");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Thread thread = new Thread(task);
        thread.start();
    }
}
جیسا کہ آپ دیکھ سکتے ہیں، ہمارے پاس کچھ کام ہے جو 60 سیکنڈ تک انتظار کرتا ہے، جس کے بعد پروگرام ختم ہو جاتا ہے۔ ہم " " کمانڈ کا استعمال کرتے ہوئے مرتب کرتے ہیں اور پھر " " javac HelloWorldApp.javaکا استعمال کرکے پروگرام چلاتے ہیں ۔ java HelloWorldAppپروگرام کو الگ ونڈو میں شروع کرنا بہتر ہے۔ مثال کے طور پر، ونڈوز پر، یہ اس طرح ہے: start java HelloWorldApp. ہم PID (process ID) حاصل کرنے کے لیے jps کمانڈ استعمال کرتے ہیں، اور ہم دھاگوں کی فہرست کو " کے ساتھ کھولتے ہیں jvisualvm --openpid pid: ایک ساتھ بہتر: جاوا اور تھریڈ کلاس۔  حصہ دوم - ہم آہنگی - 3جیسا کہ آپ دیکھ سکتے ہیں، ہمارے تھریڈ میں اب "سونے" کا درجہ ہے۔ ہمارے تھریڈ میں میٹھے خواب ہیں:
try {
	TimeUnit.SECONDS.sleep(60);
	System.out.println("Woke up");
} catch (InterruptedException e) {
	e.printStackTrace();
}
کیا آپ نے دیکھا کہ ہم InterruptedExceptionہر جگہ ہینڈل کر رہے ہیں؟ آئیے سمجھتے ہیں کیوں۔

Thread.interrupt()

بات یہ ہے کہ جب کوئی دھاگہ انتظار کر رہا ہے/سو رہا ہے، تو کوئی مداخلت کرنا چاہتا ہے۔ اس صورت میں، ہم ایک کو ہینڈل کرتے ہیں InterruptedException. اس طریقہ کار کو Thread.stop()فرسودہ یعنی فرسودہ اور ناپسندیدہ قرار دینے کے بعد بنایا گیا تھا۔ وجہ یہ تھی کہ جب stop()طریقہ کو بلایا گیا تھا تو تھریڈ کو صرف "قتل" کردیا گیا تھا جو کہ بہت غیر متوقع تھا۔ ہم نہیں جان سکے کہ تھریڈ کب روکا جائے گا، اور ہم ڈیٹا کی مستقل مزاجی کی ضمانت نہیں دے سکے۔ تصور کریں کہ آپ فائل میں ڈیٹا لکھ رہے ہیں جب تھریڈ مارا گیا ہے۔ دھاگے کو مارنے کے بجائے، جاوا کے تخلیق کاروں نے فیصلہ کیا کہ اسے یہ بتانا زیادہ منطقی ہوگا کہ اس میں خلل ڈالنا چاہیے۔ اس معلومات کا جواب کیسے دینا ہے اس کا فیصلہ تھریڈ کو کرنا ہے۔ مزید تفصیلات کے لیے پڑھیں Thread.stop کیوں فرسودہ ہے؟ اوریکل کی ویب سائٹ پر۔ آئیے ایک مثال دیکھتے ہیں:
public static void main(String []args) {
	Runnable task = () -> {
		try {
			TimeUnit.SECONDS.sleep(60);
		} catch (InterruptedException e) {
			System.out.println("Interrupted");
		}
	};
	Thread thread = new Thread(task);
	thread.start();
	thread.interrupt();
}
اس مثال میں، ہم 60 سیکنڈ انتظار نہیں کریں گے۔ اس کے بجائے، ہم فوری طور پر "انٹرپٹڈ" ڈسپلے کریں گے۔ اس کی وجہ یہ ہے کہ ہم نے interrupt()تھریڈ پر طریقہ کہا ہے۔ یہ طریقہ ایک اندرونی جھنڈا سیٹ کرتا ہے جسے "انٹرپٹ سٹیٹس" کہا جاتا ہے۔ یعنی ہر تھریڈ میں ایک اندرونی جھنڈا ہوتا ہے جو براہ راست قابل رسائی نہیں ہوتا۔ لیکن ہمارے پاس اس جھنڈے کے ساتھ بات چیت کرنے کے مقامی طریقے ہیں۔ لیکن یہ واحد راستہ نہیں ہے۔ ایک دھاگہ چل رہا ہے، کسی چیز کا انتظار نہیں کر رہا ہے، صرف اعمال انجام دے رہا ہے۔ لیکن یہ اندازہ لگا سکتا ہے کہ دوسرے اس کے کام کو ایک خاص وقت پر ختم کرنا چاہیں گے۔ مثال کے طور پر:
public static void main(String []args) {
	Runnable task = () -> {
		while(!Thread.currentThread().isInterrupted()) {
			// Do some work
		}
		System.out.println("Finished");
	};
	Thread thread = new Thread(task);
	thread.start();
	thread.interrupt();
}
اوپر دی گئی مثال میں، whileلوپ کو اس وقت تک عمل میں لایا جائے گا جب تک کہ تھریڈ کو بیرونی طور پر روکا نہ جائے۔ جہاں تک isInterruptedپرچم کا تعلق ہے، یہ جاننا ضروری ہے کہ اگر ہم ایک کو پکڑتے ہیں InterruptedException، تو isInterrupted جھنڈا ری سیٹ ہو جاتا ہے، اور پھر isInterrupted()غلط ہو جائے گا۔ تھریڈ کلاس میں ایک جامد Thread.interrupted() طریقہ بھی ہے جو صرف موجودہ تھریڈ پر لاگو ہوتا ہے، لیکن یہ طریقہ جھنڈے کو غلط پر سیٹ کرتا ہے! تھریڈ انٹرپشن کے عنوان سے اس باب میں مزید پڑھیں ۔

شامل ہوں (دوسرے دھاگے کے ختم ہونے کا انتظار کریں)

انتظار کی سادہ ترین قسم دوسرے تھریڈ کے ختم ہونے کا انتظار کر رہی ہے۔
public static void main(String []args) throws InterruptedException {
	Runnable task = () -> {
		try {
			TimeUnit.SECONDS.sleep(5);
		} catch (InterruptedException e) {
			System.out.println("Interrupted");
		}
	};
	Thread thread = new Thread(task);
	thread.start();
	thread.join();
	System.out.println("Finished");
}
اس مثال میں، نیا تھریڈ 5 سیکنڈ سوئے گا۔ ایک ہی وقت میں، مرکزی دھاگہ اس وقت تک انتظار کرے گا جب تک کہ سوئے ہوئے دھاگے کے جاگ نہ جائیں اور اپنا کام مکمل کر لیں۔ اگر آپ JVisualVM میں تھریڈ کی حالت دیکھیں تو یہ اس طرح نظر آئے گا: ایک ساتھ بہتر: جاوا اور تھریڈ کلاس۔  حصہ II - ہم آہنگی - 4مانیٹرنگ ٹولز کا شکریہ، آپ دیکھ سکتے ہیں کہ تھریڈ کے ساتھ کیا ہو رہا ہے۔ یہ joinطریقہ بہت آسان ہے، کیونکہ یہ جاوا کوڈ کے ساتھ صرف ایک طریقہ ہے جو اس وقت تک چلتا ہے wait()جب تک کہ وہ تھریڈ جس پر اسے کہا جاتا ہے زندہ ہو۔ جیسے ہی دھاگہ مر جاتا ہے (جب یہ اپنے کام سے فارغ ہو جاتا ہے) تو انتظار میں خلل پڑتا ہے۔ اور یہ طریقہ کا سارا جادو ہے join()۔ تو آئیے سب سے دلچسپ چیز کی طرف چلتے ہیں۔

مانیٹر

ملٹی تھریڈنگ میں مانیٹر کا تصور شامل ہے۔ مانیٹر کا لفظ انگریزی میں 16ویں صدی کے لاطینی زبان میں آتا ہے اور اس کا مطلب ہے "ایک ایسا آلہ یا آلہ جو کسی عمل کا مشاہدہ، جانچ یا مسلسل ریکارڈ رکھنے کے لیے استعمال ہوتا ہے"۔ اس مضمون کے تناظر میں، ہم بنیادی باتوں کا احاطہ کرنے کی کوشش کریں گے۔ کوئی بھی جو تفصیلات چاہتا ہے، براہ کرم منسلک مواد میں غوطہ لگائیں۔ ہم اپنے سفر کا آغاز جاوا لینگویج اسپیسیفیکیشن (JLS): 17.1 کے ساتھ کرتے ہیں۔ ہم وقت سازی یہ مندرجہ ذیل کہتا ہے: ایک ساتھ بہتر: جاوا اور تھریڈ کلاس۔  حصہ II - ہم آہنگی - 5یہ پتہ چلتا ہے کہ جاوا دھاگوں کے درمیان ہم آہنگی کے لئے "مانیٹر" میکانزم استعمال کرتا ہے۔ ایک مانیٹر ہر شے کے ساتھ منسلک ہوتا ہے، اور تھریڈز اسے حاصل کر سکتے ہیں lock()یا اس کے ساتھ چھوڑ سکتے ہیں unlock()۔ اگلا، ہم اوریکل ویب سائٹ پر ٹیوٹوریل تلاش کریں گے: Intrinsic Locks and Synchronization ۔ اس ٹیوٹوریل میں کہا گیا ہے کہ جاوا کی مطابقت پذیری ایک اندرونی وجود کے گرد بنائی گئی ہے جسے اندرونی لاک یا مانیٹر لاک کہا جاتا ہے ۔ اس تالا کو اکثر صرف " مانیٹر " کہا جاتا ہے۔ ہم دوبارہ یہ بھی دیکھتے ہیں کہ جاوا میں ہر شے کے ساتھ ایک اندرونی تالا منسلک ہوتا ہے۔ آپ Java - Intrinsic Locks اور Synchronization کو پڑھ سکتے ہیں ۔ اس کے بعد یہ سمجھنا ضروری ہو گا کہ جاوا میں کسی چیز کو مانیٹر کے ساتھ کیسے جوڑا جا سکتا ہے۔ جاوا میں، ہر آبجیکٹ کا ایک ہیڈر ہوتا ہے جو اندرونی میٹا ڈیٹا کو اسٹور کرتا ہے جو کوڈ سے پروگرامر کے لیے دستیاب نہیں ہوتا، لیکن جسے ورچوئل مشین کو اشیاء کے ساتھ صحیح طریقے سے کام کرنے کی ضرورت ہوتی ہے۔ آبجیکٹ ہیڈر میں ایک "نشان لفظ" شامل ہے، جو اس طرح لگتا ہے: ایک ساتھ بہتر: جاوا اور تھریڈ کلاس۔  حصہ II - ہم آہنگی - 6

https://edu.netbeans.org/contrib/slides/java-overview-and-java-se6.pdf

یہاں جاوا ورلڈ کا ایک مضمون ہے جو بہت مفید ہے: جاوا ورچوئل مشین تھریڈ سنکرونائزیشن کو کیسے انجام دیتی ہے ۔ اس مضمون کو JDK بگ ٹریکنگ سسٹم میں درج ذیل شمارے کے "خلاصہ" سیکشن کی تفصیل کے ساتھ ملایا جانا چاہیے: JDK-8183909 ۔ آپ وہی چیز یہاں پڑھ سکتے ہیں: JEP-8183909 ۔ لہذا، جاوا میں، ایک مانیٹر کسی شے کے ساتھ منسلک ہوتا ہے اور جب تھریڈ لاک کو حاصل کرنے (یا حاصل کرنے) کی کوشش کرتا ہے تو دھاگے کو روکنے کے لیے استعمال ہوتا ہے۔ یہاں سب سے آسان مثال ہے:
public class HelloWorld{
    public static void main(String []args){
        Object object = new Object();
        synchronized(object) {
            System.out.println("Hello World");
        }
    }
}
یہاں، موجودہ تھریڈ (جس پر کوڈ کی ان لائنوں پر عمل درآمد ہوتا ہے) synchronizedکلیدی لفظ کا استعمال کرتا ہے تاکہ متغیر سے وابستہ مانیٹر کو object"\تالا حاصل کرنے/حاصل کرنے کی کوشش کرے۔ اگر کوئی اور مانیٹر کے لیے مقابلہ نہیں کر رہا ہے (یعنی کوئی اور ایک ہی چیز کا استعمال کرتے ہوئے سنکرونائزڈ کوڈ نہیں چلا رہا ہے)، تو جاوا ایک اصلاح کرنے کی کوشش کر سکتا ہے جسے "متعصب لاکنگ" کہا جاتا ہے۔ ایک متعلقہ ٹیگ اور ایک ریکارڈ جس کے بارے میں مانیٹر کے لاک کا دھاگہ مالک ہے آبجیکٹ ہیڈر میں نشان والے لفظ میں شامل کیا جاتا ہے۔ یہ مانیٹر کو لاک کرنے کے لیے درکار اوور ہیڈ کو کم کرتا ہے۔ اگر مانیٹر پہلے کسی دوسرے دھاگے کی ملکیت میں تھا، تو اس طرح کا تالا لگانا کافی نہیں ہے۔ JVM لاکنگ کی اگلی قسم پر سوئچ کرتا ہے: "بنیادی لاکنگ"۔ یہ موازنہ اور تبادلہ (CAS) آپریشنز کا استعمال کرتا ہے۔ مزید کیا ہے، آبجیکٹ ہیڈر کا نشان والا لفظ خود نشان والے لفظ کو مزید ذخیرہ نہیں کرتا ہے، بلکہ اس کا حوالہ دیتا ہے کہ اسے کہاں ذخیرہ کیا گیا ہے، اور ٹیگ تبدیل ہوتا ہے تاکہ JVM سمجھے کہ ہم بنیادی لاکنگ استعمال کر رہے ہیں۔ اگر ایک سے زیادہ تھریڈز ایک مانیٹر کے لیے مقابلہ (مقابلہ) کرتے ہیں (ایک نے تالا حاصل کر لیا ہے، اور دوسرا تالے کے جاری ہونے کا انتظار کر رہا ہے)، تو نشان والے لفظ میں ٹیگ تبدیل ہو جاتا ہے، اور نشان کا لفظ اب مانیٹر کا حوالہ محفوظ کرتا ہے۔ ایک آبجیکٹ کے طور پر - JVM کی کچھ اندرونی ہستی۔ جیسا کہ JDK Enchancement Proposal (JEP) میں بتایا گیا ہے، اس صورت حال کو اس ہستی کو ذخیرہ کرنے کے لیے میموری کے مقامی ہیپ ایریا میں جگہ کی ضرورت ہوتی ہے۔ اس اندرونی ہستی کے میموری مقام کا حوالہ آبجیکٹ ہیڈر کے نشان والے لفظ میں محفوظ کیا جائے گا۔ اس طرح، ایک مانیٹر واقعی ایک سے زیادہ دھاگوں کے درمیان مشترکہ وسائل تک رسائی کو ہم آہنگ کرنے کا ایک طریقہ کار ہے۔ JVM اس میکانزم کے متعدد نفاذ کے درمیان سوئچ کرتا ہے۔ لہذا، سادگی کے لئے، مانیٹر کے بارے میں بات کرتے وقت، ہم اصل میں تالے کے بارے میں بات کر رہے ہیں. ایک ساتھ بہتر: جاوا اور تھریڈ کلاس۔  حصہ II - ہم آہنگی - 7

مطابقت پذیر (تالے کا انتظار)

جیسا کہ ہم نے پہلے دیکھا، "مطابقت پذیر بلاک" (یا "تنقیدی سیکشن") کا تصور مانیٹر کے تصور سے گہرا تعلق ہے۔ ایک مثال پر نظر ڈالیں:
public static void main(String[] args) throws InterruptedException {
	Object lock = new Object();

	Runnable task = () -> {
		synchronized(lock) {
			System.out.println("thread");
		}
	};

	Thread th1 = new Thread(task);
	th1.start();
	synchronized(lock) {
		for (int i = 0; i < 8; i++) {
			Thread.currentThread().sleep(1000);
			System.out.print(" " + i);
		}
		System.out.println(" ...");
	}
}
یہاں، مرکزی دھاگہ پہلے ٹاسک آبجیکٹ کو نئے تھریڈ میں منتقل کرتا ہے، اور پھر فوری طور پر لاک حاصل کر لیتا ہے اور اس کے ساتھ ایک طویل آپریشن کرتا ہے (8 سیکنڈ)۔ اس سارے وقت میں، کام آگے بڑھنے سے قاصر ہے، کیونکہ یہ synchronizedبلاک میں داخل نہیں ہو سکتا، کیونکہ لاک پہلے ہی حاصل کر لیا گیا ہے۔ اگر دھاگے کو تالا نہیں مل سکتا ہے، تو یہ مانیٹر کا انتظار کرے گا۔ جیسے ہی اسے لاک مل جائے گا، اس پر عملدرآمد جاری رہے گا۔ جب ایک دھاگہ مانیٹر سے باہر نکلتا ہے، تو یہ لاک کو جاری کرتا ہے۔ JVisualVM میں، یہ اس طرح لگتا ہے: ایک ساتھ بہتر: جاوا اور تھریڈ کلاس۔  حصہ II - ہم آہنگی - 8جیسا کہ آپ JVisualVM میں دیکھ سکتے ہیں، اسٹیٹس "مانیٹر" ہے، یعنی تھریڈ بلاک ہے اور مانیٹر نہیں لے سکتا۔ آپ تھریڈ کی حیثیت کا تعین کرنے کے لیے کوڈ کا استعمال بھی کر سکتے ہیں، لیکن اس طرح سے طے شدہ سٹیٹس کے نام JVisualVM میں استعمال ہونے والے ناموں سے میل نہیں کھاتے، حالانکہ وہ ایک جیسے ہیں۔ اس صورت میں، th1.getState()لوپ میں بیان BLOCKED واپس آجائے گا ، کیونکہ جب تک لوپ چل رہا ہے، lockآبجیکٹ کے مانیٹر پر mainتھریڈ کا قبضہ ہوتا ہے، اور th1تھریڈ بلاک ہوجاتا ہے اور لاک کے جاری ہونے تک آگے نہیں بڑھ سکتا۔ مطابقت پذیر بلاکس کے علاوہ، ایک مکمل طریقہ مطابقت پذیر کیا جا سکتا ہے. مثال کے طور پر، یہاں HashTableکلاس سے ایک طریقہ ہے:
public synchronized int size() {
	return count;
}
یہ طریقہ کسی بھی وقت صرف ایک تھریڈ کے ذریعے عمل میں لایا جائے گا۔ کیا ہمیں واقعی تالا کی ضرورت ہے؟ ہاں، ہمیں اس کی ضرورت ہے۔ مثال کے طریقوں کی صورت میں، "یہ" آبجیکٹ (موجودہ آبجیکٹ) ایک تالے کے طور پر کام کرتا ہے۔ اس موضوع پر یہاں ایک دلچسپ بحث ہے: کیا مطابقت پذیر بلاک کے بجائے مطابقت پذیر طریقہ استعمال کرنے کا کوئی فائدہ ہے؟ . اگر طریقہ جامد ہے، تو تالا "یہ" آبجیکٹ نہیں ہوگا (کیونکہ جامد طریقہ کے لیے کوئی "یہ" آبجیکٹ نہیں ہے)، بلکہ کلاس آبجیکٹ (مثال کے طور پر، ) Integer.class۔

انتظار کریں (مانیٹر کا انتظار کریں)۔ مطلع () اور تمام () طریقوں کو اطلاع دیں۔

تھریڈ کلاس میں انتظار کا ایک اور طریقہ ہے جو مانیٹر سے وابستہ ہے۔ برعکس sleep()اور join()، اس طریقہ کو محض نہیں کہا جا سکتا۔ اس کا نام ہے wait(). طریقہ waitمانیٹر کے ساتھ منسلک آبجیکٹ پر کہا جاتا ہے جس کا ہم انتظار کرنا چاہتے ہیں۔ آئیے ایک مثال دیکھتے ہیں:
public static void main(String []args) throws InterruptedException {
	    Object lock = new Object();
	    // The task object will wait until it is notified via lock
	    Runnable task = () -> {
	        synchronized(lock) {
	            try {
	                lock.wait();
	            } catch(InterruptedException e) {
	                System.out.println("interrupted");
	            }
	        }
	        // After we are notified, we will wait until we can acquire the lock
	        System.out.println("thread");
	    };
	    Thread taskThread = new Thread(task);
	    taskThread.start();
        // We sleep. Then we acquire the lock, notify, and release the lock
	    Thread.currentThread().sleep(3000);
	    System.out.println("main");
	    synchronized(lock) {
	        lock.notify();
	    }
}
JVisualVM میں، یہ اس طرح لگتا ہے: یہ ایک ساتھ بہتر: جاوا اور تھریڈ کلاس۔  حصہ دوم - ہم آہنگی - 10سمجھنے کے لیے کہ یہ کیسے کام کرتا ہے، یاد رکھیں کہ wait()اور notify()طریقے اس سے وابستہ ہیں java.lang.Object۔ یہ عجیب معلوم ہوسکتا ہے کہ دھاگے سے متعلق طریقے Objectکلاس میں ہیں۔ لیکن اس کی وجہ اب سامنے آ رہی ہے۔ آپ کو یاد ہوگا کہ جاوا میں ہر آبجیکٹ کا ہیڈر ہوتا ہے۔ ہیڈر میں گھر کی دیکھ بھال کی مختلف معلومات ہوتی ہیں، بشمول مانیٹر کے بارے میں معلومات، یعنی تالے کی حالت۔ یاد رکھیں، ہر شے، یا کلاس کی مثال، JVM میں ایک اندرونی وجود سے منسلک ہوتی ہے، جسے اندرونی تالا یا مانیٹر کہتے ہیں۔ اوپر کی مثال میں، ٹاسک آبجیکٹ کا کوڈ اشارہ کرتا ہے کہ ہم آبجیکٹ کے ساتھ منسلک مانیٹر کے لیے مطابقت پذیر بلاک داخل کرتے ہیں lock۔ اگر ہم اس مانیٹر کے لیے تالا حاصل کرنے میں کامیاب ہو جاتے ہیں، تو wait()کہا جاتا ہے۔ ٹاسک کو انجام دینے والا تھریڈ lockآبجیکٹ کے مانیٹر کو جاری کرے گا، لیکن دھاگوں کی قطار میں داخل ہو جائے گا جو lockآبجیکٹ کے مانیٹر سے اطلاع کا انتظار کر رہے ہیں۔ دھاگوں کی اس قطار کو WAIT SET کہا جاتا ہے، جو اپنے مقصد کی زیادہ صحیح عکاسی کرتا ہے۔ یعنی، یہ قطار سے زیادہ سیٹ ہے۔ تھریڈ mainٹاسک آبجیکٹ کے ساتھ ایک نیا تھریڈ بناتا ہے، اسے شروع کرتا ہے، اور 3 سیکنڈ انتظار کرتا ہے۔ اس سے اس بات کا بہت زیادہ امکان ہوتا ہے کہ نیا تھریڈ تھریڈ سے پہلے لاک حاصل کر سکے گا main، اور مانیٹر کی قطار میں آ جائے گا۔ اس کے بعد، mainتھریڈ خود lockآبجیکٹ کے سنکرونائزڈ بلاک میں داخل ہوتا ہے اور مانیٹر کا استعمال کرتے ہوئے تھریڈ نوٹیفکیشن کرتا ہے۔ نوٹیفکیشن بھیجے جانے کے بعد، mainتھریڈ آبجیکٹ کے مانیٹر کو جاری کرتا ہے lock، اور نیا تھریڈ، جو پہلے lockآبجیکٹ کے مانیٹر کے جاری ہونے کا انتظار کر رہا تھا، عملدرآمد جاری رکھتا ہے۔ notify()صرف ایک دھاگے ( ) پر یا بیک وقت قطار میں موجود تمام دھاگوں کو اطلاع بھیجنا ممکن ہے notifyAll()۔ یہاں مزید پڑھیں: جاوا میں notify() اور notifyAll() کے درمیان فرق ۔ یہ نوٹ کرنا ضروری ہے کہ نوٹیفکیشن آرڈر اس بات پر منحصر ہے کہ JVM کو کیسے لاگو کیا جاتا ہے۔ یہاں مزید پڑھیں: نوٹیفائی اور نوٹیفائی آل کے ساتھ بھوک کو کیسے حل کیا جائے؟ . ہم وقت سازی کسی چیز کی وضاحت کیے بغیر کی جا سکتی ہے۔ آپ یہ اس وقت کرسکتے ہیں جب کوڈ کے ایک بلاک کے بجائے پورا طریقہ مطابقت پذیر ہو۔ مثال کے طور پر، جامد طریقوں کے لیے، تالا ایک کلاس آبجیکٹ ہوگا (بذریعہ حاصل کیا گیا .class):
public static synchronized void printA() {
	System.out.println("A");
}
public static void printB() {
	synchronized(HelloWorld.class) {
		System.out.println("B");
	}
}
تالے استعمال کرنے کے معاملے میں، دونوں طریقے ایک جیسے ہیں۔ اگر کوئی طریقہ جامد نہیں ہے، تو ہم وقت سازی موجودہ کا استعمال کرتے ہوئے کی جائے گی instance، یعنی استعمال کرتے ہوئے this۔ ویسے، ہم نے پہلے کہا تھا کہ آپ getState()تھریڈ کا سٹیٹس حاصل کرنے کے لیے طریقہ استعمال کر سکتے ہیں۔ مثال کے طور پر، مانیٹر کے انتظار میں قطار میں موجود تھریڈ کے لیے، حالت WAITING یا TIMED_WAITING ہو گی، اگر طریقہ نے wait()ٹائم آؤٹ کی وضاحت کی ہے۔ ایک ساتھ بہتر: جاوا اور تھریڈ کلاس۔  حصہ II - ہم آہنگی - 11

https://stackoverflow.com/questions/36425942/what-is-the-lifecycle-of-thread-in-java

تھریڈ لائف سائیکل

اس کی زندگی کے دوران، ایک دھاگے کی حیثیت بدل جاتی ہے. درحقیقت، یہ تبدیلیاں دھاگے کے لائف سائیکل پر مشتمل ہیں۔ جیسے ہی کوئی تھریڈ بنتا ہے، اس کی حیثیت NEW ہو جاتی ہے۔ اس حالت میں، نیا تھریڈ ابھی تک نہیں چل رہا ہے اور جاوا تھریڈ شیڈولر کو ابھی تک اس کے بارے میں کچھ معلوم نہیں ہے۔ تھریڈ شیڈولر کو تھریڈ کے بارے میں جاننے کے لیے، آپ کو thread.start()طریقہ کو کال کرنا چاہیے۔ پھر تھریڈ RUNNABLE حالت میں منتقل ہو جائے گا۔ انٹرنیٹ میں بہت سارے غلط خاکے ہیں جو "رن ایبل" اور "رننگ" حالتوں میں فرق کرتے ہیں۔ لیکن یہ ایک غلطی ہے، کیونکہ جاوا "کام کرنے کے لیے تیار" (رن کے قابل) اور "کام کرنے" (چلنے) میں فرق نہیں کرتا ہے۔ جب کوئی دھاگہ زندہ ہے لیکن فعال نہیں ہے (چلانے کے قابل نہیں ہے)، تو یہ دو حالتوں میں سے ایک میں ہے:
  • بلاک - ایک اہم حصے میں داخل ہونے کا انتظار کر رہے ہیں، یعنی ایک synchronizedبلاک۔
  • انتظار کرنا - کسی شرط کو پورا کرنے کے لیے دوسرے تھریڈ کا انتظار۔
اگر شرط پوری ہوجاتی ہے، تو تھریڈ شیڈولر تھریڈ شروع کرتا ہے۔ اگر تھریڈ ایک مخصوص وقت تک انتظار کر رہا ہے، تو اس کی حیثیت TIMED_WAITING ہے۔ اگر تھریڈ اب نہیں چل رہا ہے (یہ ختم ہو گیا ہے یا ایک استثناء پھینک دیا گیا تھا)، تو یہ ختم شدہ حالت میں داخل ہو جاتا ہے۔ تھریڈ کی حالت معلوم کرنے کے لیے، getState()طریقہ استعمال کریں۔ تھریڈز کا ایک isAlive()طریقہ بھی ہوتا ہے، جو دھاگہ ختم نہ ہونے کی صورت میں درست ہو جاتا ہے۔

لاک سپورٹ اور تھریڈ پارکنگ

جاوا 1.6 کے ساتھ شروع کرتے ہوئے، لاک سپورٹ نامی ایک دلچسپ طریقہ کار ظاہر ہوا۔ ایک ساتھ بہتر: جاوا اور تھریڈ کلاس۔  حصہ دوم - ہم آہنگی - 12یہ کلاس ہر اس تھریڈ کے ساتھ ایک "اجازت" جوڑتی ہے جو اسے استعمال کرتا ہے۔ طریقہ کار پر کال park()فوری طور پر واپس آتی ہے اگر اجازت نامہ دستیاب ہو، اس عمل میں اجازت نامہ استعمال کریں۔ دوسری صورت میں، یہ بلاک کرتا ہے. طریقہ کو کال کرنے سے unparkاجازت مل جاتی ہے اگر یہ ابھی تک دستیاب نہیں ہے۔ صرف 1 اجازت نامہ ہے۔ جاوا دستاویزات کلاس LockSupportسے مراد ہے Semaphore۔ آئیے ایک سادہ مثال دیکھیں:
import java.util.concurrent.Semaphore;
public class HelloWorldApp{

    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(0);
        try {
            semaphore.acquire();
        } catch (InterruptedException e) {
            // Request the permit and wait until we get it
            e.printStackTrace();
        }
        System.out.println("Hello, World!");
    }
}
یہ کوڈ ہمیشہ انتظار کرے گا، کیونکہ اب سیمفور کے پاس 0 پرمٹ ہیں۔ اور جب acquire()کوڈ میں کال کی جاتی ہے (یعنی پرمٹ کی درخواست کریں)، تو تھریڈ اس وقت تک انتظار کرتا ہے جب تک اسے اجازت نہیں مل جاتی۔ چونکہ ہم انتظار کر رہے ہیں، ہمیں ہینڈل کرنا ہوگا InterruptedException. دلچسپ بات یہ ہے کہ سیمفور کو ایک الگ تھریڈ سٹیٹ ملتا ہے۔ اگر ہم JVisualVM میں دیکھیں تو ہم دیکھیں گے کہ ریاست "انتظار" نہیں بلکہ "پارک" ہے۔ ایک ساتھ بہتر: جاوا اور تھریڈ کلاس۔  حصہ II - ہم آہنگی - 13آئیے ایک اور مثال دیکھتے ہیں:
public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            // Park the current thread
            System.err.println("Will be Parked");
            LockSupport.park();
            // As soon as we are unparked, we will start to act
            System.err.println("Unparked");
        };
        Thread th = new Thread(task);
        th.start();
        Thread.currentThread().sleep(2000);
        System.err.println("Thread state: " + th.getState());

        LockSupport.unpark(th);
        Thread.currentThread().sleep(2000);
}
تھریڈ کی حیثیت WAITING ہوگی، لیکن JVisualVM کلیدی لفظ اور کلاس کے درمیان waitفرق synchronizedکرتا parkہے LockSupport۔ یہ LockSupportاتنا اہم کیوں ہے؟ ہم دوبارہ جاوا دستاویزات کی طرف مڑتے ہیں اور WAITING دھاگے کی حالت کو دیکھتے ہیں۔ جیسا کہ آپ دیکھ سکتے ہیں، اس میں داخل ہونے کے صرف تین طریقے ہیں۔ ان میں سے دو طریقے ہیں wait()اور join(). اور تیسرا ہے LockSupport۔ جاوا میں، تالے بھی t پر بنائے جاسکتے ہیں LockSupporاور اعلیٰ سطح کے اوزار پیش کرتے ہیں۔ آئیے ایک استعمال کرنے کی کوشش کریں۔ مثال کے طور پر، پر ایک نظر ڈالیں ReentrantLock:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class HelloWorld{

    public static void main(String []args) throws InterruptedException {
        Lock lock = new ReentrantLock();
        Runnable task = () -> {
            lock.lock();
            System.out.println("Thread");
            lock.unlock();
        };
        lock.lock();

        Thread th = new Thread(task);
        th.start();
        System.out.println("main");
        Thread.currentThread().sleep(2000);
        lock.unlock();
    }
}
جیسا کہ پچھلی مثالوں میں، یہاں سب کچھ آسان ہے۔ اعتراض lockکسی کے مشترکہ وسائل کو جاری کرنے کا انتظار کرتا ہے۔ اگر ہم JVisualVM میں دیکھیں تو ہم دیکھیں گے کہ نیا تھریڈ اس وقت تک کھڑا رہے گا جب تک کہ mainتھریڈ اس پر لاک جاری نہیں کر دیتا۔ آپ یہاں تالے کے بارے میں مزید پڑھ سکتے ہیں: Java 8 StampedLocks بمقابلہ ReadWriteLocks اور Synchronized and Lock API جاوا میں۔ تالے کو کس طرح لاگو کیا جاتا ہے اس کو بہتر طور پر سمجھنے کے لیے، اس مضمون میں Phaser کے بارے میں پڑھنا مفید ہے: Java Phaser کے لیے رہنما ۔ اور مختلف سنکرونائزرز کے بارے میں بات کرتے ہوئے، آپ کو جاوا سنکرونائزرز پر DZone کا مضمون ضرور پڑھنا چاہیے۔

نتیجہ

اس جائزے میں، ہم نے جاوا میں دھاگوں کے تعامل کے اہم طریقوں کا جائزہ لیا۔ اضافی مواد: ایک ساتھ بہتر: جاوا اور تھریڈ کلاس۔ حصہ اول — عمل درآمد کے دھاگے ایک ساتھ بہتر: جاوا اور تھریڈ کلاس۔ حصہ III - ایک دوسرے کے ساتھ بہتر تعامل: جاوا اور تھریڈ کلاس۔ حصہ چہارم — کال کے قابل، مستقبل، اور دوست ایک ساتھ بہتر: جاوا اور تھریڈ کلاس۔ حصہ V — ایگزیکیوٹر، تھریڈ پول، فورک/ جوائن بیٹر ایک ساتھ: جاوا اور تھریڈ کلاس۔ حصہ VI - آگ دور!
تبصرے
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION