JavaRush /جاوا بلاگ /Random-UR /آپ دھاگے کے ساتھ جاوا کو برباد نہیں کر سکتے: حصہ II - ہم ...
Viacheslav
سطح

آپ دھاگے کے ساتھ جاوا کو برباد نہیں کر سکتے: حصہ II - ہم آہنگی۔

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

تعارف

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

پیداوار

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

نیند - سوتے ہوئے دھاگہ

ایک دھاگہ اس کے نفاذ کے دوران سو سکتا ہے۔ یہ دوسرے دھاگوں کے ساتھ تعامل کی سب سے آسان قسم ہے۔ وہ آپریٹنگ سسٹم جس پر جاوا ورچوئل مشین انسٹال ہوتی ہے، جہاں جاوا کوڈ ایکسیکیوٹ ہوتا ہے، اس کا اپنا تھریڈ شیڈیولر ہوتا ہے، جسے تھریڈ شیڈیولر کہتے ہیں۔ وہی فیصلہ کرتا ہے کہ کون سا تھریڈ کب چلانا ہے۔ پروگرامر جاوا کوڈ سے براہ راست اس شیڈیولر کے ساتھ بات چیت نہیں کر سکتا، لیکن وہ JVM کے ذریعے شیڈیولر سے تھریڈ کو کچھ دیر کے لیے روکنے کے لیے کہہ سکتا ہے، "اسے سونے کے لیے"۔ آپ مزید مضامین " Thread.sleep() " اور " How Multithreading Works " میں پڑھ سکتے ہیں۔ مزید یہ کہ، آپ یہ جان سکتے ہیں کہ ونڈوز OS میں تھریڈز کیسے کام کرتے ہیں: " 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("Waked up");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Thread thread = new Thread(task);
        thread.start();
    }
}
جیسا کہ آپ دیکھ سکتے ہیں، ہمارے پاس ایک کام ہے جو 60 سیکنڈ انتظار کرتا ہے، جس کے بعد پروگرام ختم ہو جاتا ہے۔ ہم مرتب کرتے ہیں javac HelloWorldApp.javaاور چلاتے ہیں java HelloWorldApp۔ ایک علیحدہ ونڈو میں لانچ کرنا بہتر ہے۔ مثال کے طور پر، ونڈوز پر یہ اس طرح ہوگا: start java HelloWorldApp. jps کمانڈ کا استعمال کرتے ہوئے، ہم عمل کی PID تلاش کرتے ہیں اور دھاگوں کی فہرست کو استعمال کرتے ہوئے کھولتے ہیں jvisualvm --openpid pidПроцесса: آپ دھاگے کے ساتھ جاوا کو برباد نہیں کر سکتے: حصہ II - مطابقت پذیری - 3جیسا کہ آپ دیکھ سکتے ہیں، ہمارا تھریڈ سلیپنگ سٹیٹس میں داخل ہو گیا ہے۔ درحقیقت، موجودہ دھاگے کو سونا زیادہ خوبصورتی سے کیا جا سکتا ہے:
try {
	TimeUnit.SECONDS.sleep(60);
	System.out.println("Waked up");
} catch (InterruptedException e) {
	e.printStackTrace();
}
آپ نے شاید محسوس کیا ہے کہ ہم ہر جگہ کارروائی کرتے ہیں InterruptedException؟ آئیے سمجھتے ہیں کیوں۔

تھریڈ یا Thread.interrupt میں خلل ڈالنا

بات یہ ہے کہ جب دھاگہ نیند میں انتظار کر رہا ہے، کوئی اس انتظار میں رکاوٹ ڈالنا چاہے گا۔ اس صورت میں، ہم اس طرح کی ایک استثنا کو ہینڈل کرتے ہیں. یہ طریقہ کو Thread.stopفرسودہ قرار دینے کے بعد کیا گیا، یعنی پرانا اور استعمال کے لیے ناپسندیدہ۔ اس کی وجہ یہ تھی کہ جب طریقہ کو بلایا گیا تھا، تو stopتھریڈ کو صرف "قتل" کیا گیا تھا، جو کہ بہت غیر متوقع تھا۔ ہم نہیں جان سکے کہ بہاؤ کب روکا جائے گا، ہم ڈیٹا کی مستقل مزاجی کی ضمانت نہیں دے سکے۔ تصور کریں کہ آپ کسی فائل میں ڈیٹا لکھ رہے ہیں اور پھر ندی تباہ ہو گئی ہے۔ لہذا، انہوں نے فیصلہ کیا کہ یہ زیادہ منطقی ہوگا کہ بہاؤ کو ختم نہ کیا جائے، بلکہ اسے مطلع کیا جائے کہ اس میں خلل ڈالا جائے۔ اس پر کس طرح کا رد عمل ظاہر کرنا خود بہاؤ پر منحصر ہے۔ مزید تفصیلات اوریکل کے " Why is 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 سیکنڈ انتظار نہیں کریں گے، لیکن فوری طور پر 'Interrupted' پرنٹ کریں گے۔ اس کی وجہ یہ ہے کہ ہم نے تھریڈ کا طریقہ کہا ہے 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 flag کے بارے میں جاننے کے لیے اہم بات یہ ہے کہ اگر ہم اسے پکڑتے ہیں تو InterruptedExceptionجھنڈا isInterruptedدوبارہ ترتیب دیا جاتا ہے، اور پھر isInterruptedیہ غلط واپس آجائے گا۔ Thread کلاس کے لیے ایک جامد طریقہ بھی ہے جو صرف موجودہ تھریڈ پر لاگو ہوتا ہے - 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۔ لہذا، سب سے زیادہ دلچسپ حصہ پر چلتے ہیں.

تصور مانیٹر

ملٹی تھریڈنگ میں مانیٹر جیسی چیز ہوتی ہے۔ عام طور پر، لفظ مانیٹر کا ترجمہ لاطینی سے "اوورسیر" یا "اوورسیر" کے طور پر کیا جاتا ہے۔ اس مضمون کے فریم ورک کے اندر، ہم جوہر کو یاد رکھنے کی کوشش کریں گے، اور ان لوگوں کے لیے جو چاہتے ہیں، میں آپ سے کہتا ہوں کہ تفصیلات کے لیے لنکس سے مواد میں غوطہ لگائیں۔ آئیے اپنے سفر کا آغاز جاوا زبان کی تفصیلات کے ساتھ کرتے ہیں، یعنی JLS کے ساتھ: " 17.1. ہم آہنگی "۔ یہ مندرجہ ذیل کہتا ہے: آپ دھاگے کے ساتھ جاوا کو برباد نہیں کر سکتے: حصہ II - مطابقت پذیری - 5یہ پتہ چلتا ہے کہ دھاگوں کے درمیان ہم آہنگی کے مقصد کے لیے، جاوا ایک مخصوص طریقہ کار استعمال کرتا ہے جسے "مانیٹر" کہتے ہیں۔ ہر آبجیکٹ کے ساتھ ایک مانیٹر منسلک ہوتا ہے، اور تھریڈز اسے لاک یا انلاک کر سکتے ہیں۔ اگلا، ہمیں اوریکل ویب سائٹ پر ایک تربیتی ٹیوٹوریل ملے گا: " انٹرنسک لاک اینڈ سنکرونائزیشن "۔ یہ ٹیوٹوریل بتاتا ہے کہ جاوا میں ہم آہنگی ایک اندرونی وجود کے گرد بنائی گئی ہے جسے اندرونی تالا یا مانیٹر لاک کہا جاتا ہے۔ اکثر اس طرح کے تالا کو صرف "مانیٹر" کہا جاتا ہے۔ ہم دوبارہ یہ بھی دیکھتے ہیں کہ جاوا میں ہر شے کے ساتھ ایک اندرونی تالا جڑا ہوا ہے۔ آپ " جاوا - اندرونی تالے اور مطابقت پذیری " پڑھ سکتے ہیں۔ اگلا، یہ سمجھنا ضروری ہے کہ جاوا میں کسی چیز کو مانیٹر کے ساتھ کیسے جوڑا جا سکتا ہے۔ جاوا میں ہر آبجیکٹ کا ایک ہیڈر ہوتا ہے - ایک قسم کا اندرونی میٹا ڈیٹا جو کوڈ سے پروگرامر کے لیے دستیاب نہیں ہوتا ہے، لیکن ورچوئل مشین کو اشیاء کے ساتھ صحیح طریقے سے کام کرنے کی ضرورت ہوتی ہے۔ آبجیکٹ ہیڈر میں ایک MarkWord شامل ہے جو اس طرح لگتا ہے: آپ دھاگے کے ساتھ جاوا کو برباد نہیں کر سکتے: حصہ 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 کا کچھ اندرونی وجود۔ جیسا کہ JEP میں بتایا گیا ہے، اس صورت میں، اس ہستی کو ذخیرہ کرنے کے لیے Native Heap میموری کے علاقے میں جگہ درکار ہے۔ اس داخلی ہستی کے اسٹوریج لوکیشن کا لنک مارک ورڈ آبجیکٹ میں موجود ہوگا۔ اس طرح، جیسا کہ ہم دیکھتے ہیں، مانیٹر واقعی ایک ایسا طریقہ کار ہے جو مشترکہ وسائل تک متعدد تھریڈز کی رسائی کو یقینی بناتا ہے۔ اس میکانزم کے کئی نفاذ ہیں جن کے درمیان 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()لوپ forواپس آجائے گا BLOCKED ، کیونکہ جب لوپ چل رہا ہوتا ہے، مانیٹر پر دھاگے کا lockقبضہ ہوتا ہے ، اور دھاگے کو بلاک کر دیا جاتا ہے اور جب تک تالا واپس نہیں آ جاتا کام جاری نہیں رکھ سکتا۔ ہم وقت سازی کے بلاکس کے علاوہ، ایک پورا طریقہ ہم آہنگ کیا جا سکتا ہے۔ مثال کے طور پر، کلاس سے ایک طریقہ : mainth1HashTable
public synchronized int size() {
	return count;
}
وقت کی ایک اکائی میں، یہ طریقہ صرف ایک دھاگے کے ذریعے عمل میں لایا جائے گا۔ لیکن ہمیں ایک تالے کی ضرورت ہے، ٹھیک ہے؟ ہاں مجھے اس کی ضرورت ہے۔ آبجیکٹ کے طریقوں کے معاملے میں، تالا ہو جائے گا this. اس موضوع پر ایک دلچسپ بحث ہے: " کیا سنکرونائزڈ بلاک کے بجائے مطابقت پذیر طریقہ استعمال کرنے کا کوئی فائدہ ہے؟ "۔ اگر طریقہ جامد ہے، تو تالا نہیں ہوگا this(چونکہ جامد طریقہ کے لیے نہیں ہو سکتا ہے this)، لیکن کلاس آبجیکٹ (مثال کے طور پر، Integer.class

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

تھریڈ میں انتظار کا ایک اور طریقہ ہے، جو مانیٹر سے منسلک ہے۔ برعکس sleepاور join، اسے صرف نہیں کہا جا سکتا۔ اور اس کا نام ہے wait۔ طریقہ waitاس چیز پر عمل میں لایا جاتا ہے جس کے مانیٹر پر ہم انتظار کرنا چاہتے ہیں۔ آئیے ایک مثال دیکھتے ہیں:
public static void main(String []args) throws InterruptedException {
	    Object lock = new Object();
	    // task будет ждать, пока его не оповестят через lock
	    Runnable task = () -> {
	        synchronized(lock) {
	            try {
	                lock.wait();
	            } catch(InterruptedException e) {
	                System.out.println("interrupted");
	            }
	        }
	        // После оповещения нас мы будем ждать, пока сможем взять лок
	        System.out.println("thread");
	    };
	    Thread taskThread = new Thread(task);
	    taskThread.start();
        // Ждём и после этого забираем себе лок, оповещаем и отдаём лок
	    Thread.currentThread().sleep(3000);
	    System.out.println("main");
	    synchronized(lock) {
	        lock.notify();
	    }
}
JVisualVM میں یہ اس طرح نظر آئے گا: آپ دھاگے کے ساتھ جاوا کو برباد نہیں کر سکتے: حصہ II - مطابقت پذیری - 10یہ سمجھنے کے لیے کہ یہ کیسے کام کرتا ہے، آپ کو یاد رکھنا چاہیے کہ طریقوں کا waitحوالہ دیتے ہیں ۔ یہ عجیب لگتا ہے کہ دھاگے سے متعلق طریقے . لیکن یہاں جواب مضمر ہے۔ جیسا کہ ہمیں یاد ہے، جاوا میں ہر شے کا ایک ہیڈر ہوتا ہے۔ ہیڈر میں مختلف سروس کی معلومات شامل ہیں، بشمول مانیٹر کے بارے میں معلومات — لاک کرنے کی حالت کے بارے میں ڈیٹا۔ اور جیسا کہ ہمیں یاد ہے، ہر آبجیکٹ (یعنی ہر مثال) کا ایک اندرونی JVM ہستی کے ساتھ تعلق ہے جسے اندرونی تالا کہا جاتا ہے، جسے مانیٹر بھی کہا جاتا ہے۔ اوپر کی مثال میں، ٹاسک بیان کرتا ہے کہ ہم اس سے منسلک مانیٹر پر سنکرونائزیشن بلاک داخل کرتے ہیں ۔ اگر اس مانیٹر پر تالا حاصل کرنا ممکن ہے، تو . اس کام کو انجام دینے والا دھاگہ مانیٹر کو جاری کرے گا ، لیکن مانیٹر پر اطلاع کے منتظر دھاگوں کی قطار میں شامل ہو جائے گا ۔ دھاگوں کی اس قطار کو WAIT-SET کہا جاتا ہے، جو جوہر کو زیادہ درست طریقے سے ظاہر کرتا ہے۔ یہ قطار سے زیادہ سیٹ ہے۔ تھریڈ ٹاسک ٹاسک کے ساتھ ایک نیا تھریڈ بناتا ہے، اسے شروع کرتا ہے اور 3 سیکنڈ تک انتظار کرتا ہے۔ یہ اعلیٰ امکان کے ساتھ، دھاگے سے پہلے تالے کو پکڑنے اور مانیٹر پر قطار لگانے کے لیے ایک نئے دھاگے کی اجازت دیتا ہے۔ جس کے بعد تھریڈ خود ہی سنکرونائزیشن بلاک میں داخل ہوتا ہے اور مانیٹر پر تھریڈ کی اطلاع دیتا ہے۔ نوٹیفکیشن بھیجے جانے کے بعد، تھریڈ مانیٹر کو جاری کرتا ہے ، اور نیا دھاگہ (جو پہلے انتظار کر رہا تھا) مانیٹر کے جاری ہونے کا انتظار کرنے کے بعد جاری رہتا ہے۔ دھاگوں میں سے صرف ایک ( ) یا قطار میں موجود تمام دھاگوں کو ایک ساتھ اطلاع بھیجنا ممکن ہے ( )۔ آپ جاوا میں "notify() اور notifyAll() کے درمیان فرق " میں مزید پڑھ سکتے ہیں۔ یہ نوٹ کرنا ضروری ہے کہ نوٹیفکیشن آرڈر JVM کے نفاذ پر منحصر ہے۔ آپ " notify اور notifyall کے ساتھ بھوک کو کیسے حل کریں؟ " میں مزید پڑھ سکتے ہیں۔ ہم وقت سازی کسی چیز کی وضاحت کیے بغیر کی جا سکتی ہے۔ یہ اس وقت کیا جا سکتا ہے جب کوڈ کا الگ سیکشن مطابقت پذیر نہ ہو بلکہ ایک پورا طریقہ ہو۔ مثال کے طور پر، جامد طریقوں کے لیے لاک کلاس آبجیکٹ ہوگا (بذریعہ حاصل کیا گیا ): notifyjava.lang.ObjectObjectlockwaitlocklockmainmainmainlockmainlocklocknotifynotifyAll.class
public static synchronized void printA() {
	System.out.println("A");
}
public static void printB() {
	synchronized(HelloWorld.class) {
		System.out.println("B");
	}
}
تالے استعمال کرنے کے معاملے میں، دونوں طریقے ایک جیسے ہیں۔ اگر طریقہ جامد نہیں ہے، تو ہم وقت سازی موجودہ کے مطابق کی جائے گی instance، یعنی کے مطابق this۔ ویسے پہلے ہم نے کہا تھا کہ طریقہ استعمال کرکے getStateآپ تھریڈ کا سٹیٹس حاصل کر سکتے ہیں۔ waitتو یہاں ایک دھاگہ ہے جو مانیٹر کے ذریعہ قطار میں ہے، اگر طریقہ انتظار کے وقت کی حد متعین کرتا ہے تو حالت WAITING یا TIMED_WAITING ہوگی ۔آپ دھاگے کے ساتھ جاوا کو برباد نہیں کر سکتے: حصہ II - مطابقت پذیری - 11

ایک دھاگے کا لائف سائیکل

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

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

جاوا 1.6 کے بعد سے ایک دلچسپ طریقہ کار تھا جسے LockSupport کہتے ہیں ۔ آپ دھاگے کے ساتھ جاوا کو برباد نہیں کر سکتے: حصہ II - ہم آہنگی - 12یہ کلاس ہر اس تھریڈ کے ساتھ "اجازت" یا اجازت جوڑتی ہے جو اسے استعمال کرتا ہے۔ اگر اجازت نامہ دستیاب ہو تو میتھڈ کال parkفوراً واپس آجاتی ہے، کال کے دوران اسی پرمٹ پر قبضہ کرتے ہوئے۔ ورنہ یہ بلاک ہے۔ طریقہ کال کرنے سے unparkاجازت مل جاتی ہے اگر یہ پہلے سے دستیاب نہیں ہے۔ صرف 1 پرمٹ ہے۔ Java API میں، 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) {
            // Просим разрешение и ждём, пока не получим его
            e.printStackTrace();
        }
        System.out.println("Hello, World!");
    }
}
یہ کوڈ ہمیشہ کے لیے انتظار کرے گا کیونکہ سیمفور کے پاس اب 0 پرمٹ ہے۔ اور جب کوڈ میں کال کی جاتی ہے acquire(یعنی اجازت کی درخواست کی جاتی ہے)، تو تھریڈ اس وقت تک انتظار کرتا ہے جب تک اسے اجازت نہیں ملتی۔ چونکہ ہم انتظار کر رہے ہیں، ہم اس پر کارروائی کرنے کے پابند ہیں InterruptedException۔ دلچسپ بات یہ ہے کہ ایک سیمفور ایک الگ تھریڈ سٹیٹ کو نافذ کرتا ہے۔ اگر ہم JVisualVM میں دیکھیں تو ہم دیکھیں گے کہ ہماری ریاست انتظار نہیں بلکہ پارک ہے۔ آپ دھاگے کے ساتھ جاوا کو برباد نہیں کر سکتے: حصہ II - مطابقت پذیری - 13آئیے ایک اور مثال دیکھتے ہیں:
public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            //Запаркуем текущий поток
            System.err.println("Will be Parked");
            LockSupport.park();
            // Как только нас распаркуют - начнём действовать
            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 waitfrom synchronizedاور parkfrom کے درمیان فرق کرتا ہے LockSupport۔ یہ ایک اتنا اہم کیوں ہے LockSupport؟ آئیے جاوا API کی طرف دوبارہ رجوع کریں اور Thread State WAITING کو دیکھیں ۔ جیسا کہ آپ دیکھ سکتے ہیں، اس میں داخل ہونے کے صرف تین طریقے ہیں۔ 2 طریقے - یہ waitاور join. اور تیسرا ہے LockSupport۔ جاوا میں تالے انہی اصولوں پر بنائے گئے ہیں LockSupportاور اعلیٰ سطح کے ٹولز کی نمائندگی کرتے ہیں۔ آئیے ایک استعمال کرنے کی کوشش کریں۔ مثال کے طور پر دیکھتے ہیں 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تھریڈ اسے لاک نہیں دیتا۔ آپ یہاں تالے کے بارے میں مزید پڑھ سکتے ہیں: " جاوا 8 میں ملٹی تھریڈ پروگرامنگ۔ حصہ دو۔ متغیر اشیاء تک رسائی کو ہم وقت سازی کرنا " اور " Java Lock API۔ تھیوری اور استعمال کی مثال ۔" تالے کے نفاذ کو بہتر طور پر سمجھنے کے لیے، Phazer کے بارے میں جائزہ " Phaser Class " میں پڑھنا مفید ہے۔ اور مختلف سنکرونائزرز کے بارے میں بات کرتے ہوئے، آپ کو Habré " Java.util.concurrent.* Synchronizers Reference " پر مضمون ضرور پڑھنا چاہیے ۔

کل

اس جائزے میں، ہم نے جاوا میں تھریڈز کے تعامل کے اہم طریقوں کو دیکھا۔ اضافی مواد: #ویاچسلاو
تبصرے
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION