سلام! اسان ملٽي ٿريڊنگ جو مطالعو جاري رکون ٿا، ۽ اڄ اسان هڪ نئين لفظ سان واقف ٿينداسين - volatile ۽ yield() طريقو. اچو ته سمجهون ته اهو ڇا آهي :)
جڏهن اسان هڪ ٿريڊ تي حاصل ڪرڻ واري طريقي کي سڏيندا آهيون، اهو اصل ۾ ٻين موضوعن کي چوي ٿو: "ٺيڪ آهي، دوستو، مان ڪنهن خاص جلدي ۾ نه آهيان، تنهنڪري جيڪڏهن اهو ضروري آهي ته توهان مان ڪنهن لاء CPU وقت حاصل ڪرڻ لاء، اهو وٺو، مان آهيان. تڪڙو نه. هتي هڪ سادي مثال آهي ته اهو ڪيئن ڪم ڪري ٿو:
لفظ volatile
جڏهن ملٽي-ٽيڊڊ ايپليڪيشن ٺاهي رهيا آهيون، اسان کي ٻه سنگين مسئلن کي منهن ڏئي سگهون ٿا. پهرين، هڪ گھڻن موضوعن واري ايپليڪيشن جي آپريشن دوران، مختلف موضوعن کي ڪيش ڪري سگھن ٿا متغير جي قيمتن (اسان هن بابت وڌيڪ ڳالهائينداسين ليڪچر ۾ "استعمال volatile" ). اهو ممڪن آهي ته هڪ ٿريڊ هڪ متغير جي قيمت کي تبديل ڪيو، پر ٻئي اهو تبديلي نه ڏٺو ڇو ته اهو متغير جي پنهنجي ڪيش ڪيل ڪاپي سان ڪم ڪري رهيو هو. قدرتي طور، نتيجا سنجيده ٿي سگهي ٿو. تصور ڪريو ته اهو صرف ڪجهه قسم جو "متغير" ناهي، پر، مثال طور، توهان جي بئنڪ ڪارڊ جو بيلنس، جيڪو اوچتو بي ترتيب سان اڳتي وڌڻ شروع ڪيو :) بلڪل خوشگوار ناهي، صحيح؟ ٻيو، جاوا ۾، پڙهڻ ۽ لکڻ جي عملن تي سڀني قسمن جي شعبن کان سواءlong
۽ double
ايٽمي آهن. ايٽمي ڇا آهي؟ خير، مثال طور، جيڪڏهن توهان هڪ ٿريڊ ۾ هڪ متغير جي قيمت کي تبديل ڪريو ٿا int
، ۽ ٻئي ٿريڊ ۾ توهان هن متغير جي قيمت کي پڙهو ٿا، توهان کي يا ته ان جي پراڻي قيمت يا هڪ نئين حاصل ٿيندي - اها جيڪا تبديلي کان پوءِ نڪتي. ٿريڊ 1. ڪو به ”انٽرميڊيٽ آپشنز“ اتي ظاهر نه ٿيندو ٿي سگهي ٿو. بهرحال، اهو ڪم نٿو ڪري long
۽ . double
ڇو؟ ڇاڪاڻ ته اهو پار-پليٽ فارم آهي. ڇا توهان کي ياد آهي ته اسان پهرين سطح تي ڪيئن چيو ته جاوا اصول "هڪ ڀيرو لکيو ويو آهي، هر جڳهه ڪم ڪري ٿو"؟ هي ڪراس پليٽ فارم آهي. اهو آهي، هڪ جاوا ايپليڪيشن مڪمل طور تي مختلف پليٽ فارمن تي هلندو آهي. مثال طور، ونڊوز آپريٽنگ سسٽم تي، Linux يا MacOS جا مختلف ورجن، ۽ هر جاءِ تي هي ايپليڪيشن مستحڪم ڪم ڪندي. long
۽ double
- جاوا ۾ سڀ کان وڌيڪ "بھاري" پرائمري: اھي 64 بٽ وزن ڪن ٿا. ۽ ڪجهه 32-bit پليٽ فارم صرف 64-bit متغير پڙهڻ ۽ لکڻ جي ايٽمي کي لاڳو نٿا ڪن. اهڙا متغير پڙهيا ۽ لکيا ويندا آهن ٻن عملن ۾. پھريون، پھريون 32 بٽ متغير ڏانھن لکيل آھن، پوءِ ٻيو 32. ان مطابق، انھن صورتن ۾ ھڪڙو مسئلو پيدا ٿي سگھي ٿو. ھڪڙو ٿريڊ ڪجھ 64-bit قدر ھڪڙي متغير ڏانھن لکندو آھيХ
، ۽ هو اهو ڪري ٿو "ٻن قدمن ۾." ساڳئي وقت، ٻيو سٽ هن متغير جي قيمت کي پڙهڻ جي ڪوشش ڪري ٿو، ۽ اهو صحيح وچ ۾ ڪري ٿو، جڏهن ته پهرين 32 بٽ اڳ ۾ ئي لکيا ويا آهن، پر ٻئي اڃا تائين نه لکيا ويا آهن. نتيجي طور، اهو هڪ وچولي، غلط قدر پڙهي ٿو، ۽ هڪ غلطي ٿئي ٿي. مثال طور، جيڪڏهن اهڙي پليٽ فارم تي اسان هڪ نمبر لکڻ جي ڪوشش ڪريون هڪ متغير - 9223372036854775809 - اهو 64 بٽس تي قبضو ڪندو. بيڪن جي فارم ۾ اهو نظر اچي: 10000000000000000000000 = ٻيو هيٺ لکندو ۽ ٻيو 3200 ماڳ نگاهه: 100: 500 سيم: 100: 500 هڏپان جي اوچائي تي ويٺو. متغير جي وچولي قدر پڙهو - 10000000000000000000000000000000000000000000000000000، پهرين 32 بٽ جيڪي اڳ ۾ ئي لکيا ويا آهن. اعشاريه سسٽم ۾، هي نمبر 2147483648 جي برابر آهي. يعني، اسان صرف نمبر 9223372036854775809 کي متغير ۾ لکڻ چاهيون ٿا، پر حقيقت اها آهي ته ڪجهه پليٽ فارمن تي اهو عمل ايٽمي نه آهي، اسان کي "کاٻي" نمبر 2147483648 مليو. ، جنهن جي اسان کي ضرورت ناهي، ڪٿي به نه. ۽ اهو معلوم ناهي ته اهو پروگرام جي عمل کي ڪيئن متاثر ڪندو. ٻئي ٿريڊ کي آخر ۾ لکڻ کان اڳ صرف متغير جي قيمت پڙهو، اهو آهي، اهو پهريون 32 بٽ ڏٺو، پر ٻيو 32 بٽ نه. اهي مسئلا، يقينا، ڪالهه پيدا نه ٿيا، ۽ جاوا ۾ اهي صرف هڪ لفظ استعمال ڪندي حل ڪيا ويا آهن - volatile . جيڪڏهن اسان پنهنجي پروگرام ۾ لفظ volatile سان ڪجهه variable جو اعلان ڪريون ٿا...
public class Main {
public volatile long x = 2222222222222222222L;
public static void main(String[] args) {
}
}
... مطلب ته:
- اهو هميشه ايٽمي طور تي پڙهيو ۽ لکيو ويندو. جيتوڻيڪ اهو 64-bit
double
ياlong
. - جاوا مشين ان کي ڪيش نه ڪندي. تنهن ڪري اها صورتحال جڏهن 10 سلسلا ڪم ڪن ٿا انهن جي مقامي نقلن سان خارج ٿيل آهي.
yield() طريقو
اسان اڳ ۾ ئي ڪلاس جا ڪيترائي طريقا ڏسي چڪا آهيونThread
، پر ھڪڙو اھم آھي جيڪو توھان لاءِ نئون ھوندو. ھي آھي yield() طريقو . انگريزيءَ مان ترجمو ڪيو ويو جيئن "give in." ۽ اهو ئي آهي جيڪو طريقو ڪندو آهي! ![وهڪري جو انتظام. volatile keyword and the yield() طريقو - 2](https://cdn.javarush.com/images/article/a32c882a-5fe9-4219-a1df-1488ed9954b1/1024.jpeg)
public class ThreadExample extends Thread {
public ThreadExample() {
this.start();
}
public void run() {
System.out.println(Thread.currentThread().getName() + "give way to others");
Thread.yield();
System.out.println(Thread.currentThread().getName() + " has finished executing.");
}
public static void main(String[] args) {
new ThreadExample();
new ThreadExample();
new ThreadExample();
}
}
اسان ترتيب وار ٽي ٿريڊ ٺاهي ۽ لانچ ڪريون ٿا - Thread-0
, Thread-1
and Thread-2
. Thread-0
پهرين شروع ٿئي ٿو ۽ فوري طور تي ٻين کي رستو ڏئي ٿو. ان کان پوء اهو شروع ٿئي ٿو Thread-1
، ۽ پڻ رستو ڏئي ٿو. ان کان پوء، اهو شروع ٿئي ٿو Thread-2
، جيڪو پڻ گهٽ آهي. اسان وٽ وڌيڪ ٿريڊ نه آھن، ۽ Thread-2
آخري ھڪڙي پنھنجي جاءِ ڇڏڻ کان پوءِ، ٿريڊ شيڊيولر ڏسندو آھي: ”پوءِ، وڌيڪ نوان ٿريڊ نه آھن، اسان وٽ قطار ۾ ڪير آھي؟ آخر ڪير هو جنهن کان اڳ پنهنجي جاءِ ڇڏي ڏني Thread-2
؟ مان سمجهان ٿو ته اهو هو Thread-1
؟ چڱو، پوءِ ٿيڻ ڏيو“. Thread-1
آخر تائين پنهنجو ڪم ڪندو آهي، جنهن کان پوءِ ٿريڊ شيڊيولر کي همٿائڻ جاري آهي: ”ٺيڪ آهي، ٿريڊ-1 مڪمل ٿي چڪو آهي. ڇا اسان وٽ ٻيو ڪو به قطار ۾ آهي؟" قطار ۾ Thread-0 آهي: هن Thread-1 کان اڳ ئي پنهنجي جاءِ ڇڏي ڏني. هاڻي معاملو هن وٽ اچي ويو آهي، ۽ هن کي انجام تائين پهچايو پيو وڃي. جنهن کان پوءِ شيڊيولر ٿريڊن کي ڪوآرڊينيشن ختم ڪري ٿو: ”ٺيڪ آهي، ٿريڊ-2، توهان ٻين ٿريڊن کي رستو ڏنو، اهي سڀ اڳ ۾ ئي ڪم ڪري چڪا آهن. رستو ڏيڻ لاءِ توهان آخري هئا، تنهنڪري هاڻي توهان جي باري آهي. ان کان پوء، Thread-2 مڪمل ٿيڻ لاء هلندو آهي. ڪنسول آئوٽ پُٽ هن طرح نظر ايندو: Thread-0 ٻين کي رستو ڏئي ٿو Thread-1 ٻين کي رستو ڏئي ٿو Thread-2 ٻين کي رستو ڏئي ٿو Thread-1 تي عملدرآمد ختم ٿي چڪو آهي. Thread-0 تي عملدرآمد ختم ٿي چڪو آهي. Thread-2 تي عملدرآمد ختم ٿي چڪو آهي. ٿريڊ شيڊيولر، يقينا، موضوعن کي مختلف ترتيب ۾ هلائي سگھي ٿو (مثال طور، 0-1-2 جي بدران 2-1-0)، پر اصول ساڳيو آهي.
ٿئي ٿو- ضابطن کان اڳ
آخري شيء جيڪو اسان اڄ تي رابطو ڪنداسين " اڳ ۾ ٿئي ٿو " اصول آهي. جئين توهان اڳ ۾ ئي ڄاڻو ٿا، جاوا ۾، ٿريڊز کي انهن جي ڪم کي مڪمل ڪرڻ لاءِ وقت ۽ وسيلا مختص ڪرڻ جو گهڻو ڪم ٿريڊ شيڊيولر طرفان ڪيو ويندو آهي. انهي سان گڏ، توهان هڪ کان وڌيڪ ڀيرا ڏٺو آهي ته ڪئين موضوعن کي هڪ ترتيب واري ترتيب ۾ عمل ڪيو وڃي ٿو، ۽ اڪثر ڪري اهو اڳڪٿي ڪرڻ ناممڪن آهي. ۽ عام طور تي، "ترتيباتي" پروگرامنگ کان پوء جيڪو اسان اڳ ڪيو هو، ملٽي ٽريڊنگ هڪ بي ترتيب واري شيء وانگر نظر اچي ٿو. جيئن توهان اڳ ۾ ئي ڏٺو آهي، هڪ گھڻائي واري پروگرام جي ترقي کي ڪنٽرول ڪري سگهجي ٿو طريقن جي مڪمل سيٽ استعمال ڪندي. پر ان کان علاوه، جاوا ملٽي ٿريڊنگ ۾ هڪ ٻيو آهي ”استحکام جو ٻيٽ“ - 4 ضابطا جن کي ” ٿيڻ کان اڳ “ سڏيو ويندو آهي. لفظي طور تي انگريزيءَ مان هن جو ترجمو ڪيو ويو آهي ”ٿي ٿو اڳي“، يا ”ٿي ٿو اڳي“. انهن ضابطن جي معنيٰ سمجھڻ ۾ بلڪل سادو آهي. تصور ڪريو ته اسان وٽ ٻه سلسلا آهن -A
۽ B
. انهن سلسلي مان هر هڪ آپريشن ڪري سگهي ٿو 1
۽ 2
. ۽ جڏهن هر هڪ ضابطي ۾ اسين چئون ٿا ” A ٿئي ٿو- B کان اڳA
“، ان جو مطلب آهي ته آپريشن کان اڳ ٿريڊ ۾ ڪيل سموريون تبديليون 1
۽ اهي تبديليون جيڪي هن آپريشن ۾ شامل ڪيون ويون آهن، اهي ٿريڊ تي نظر اچن ٿيون B
جڏهن آپريشن ڪيو وڃي ٿو 2
۽ آپريشن کان پوء. انهن ضابطن مان هر هڪ کي يقيني بڻائي ٿو ته جڏهن هڪ گھڻن موضوعن تي مشتمل پروگرام لکندو، ڪجهه واقعا ٻين کان 100٪ وقت کان اڳ ٿيندا، ۽ اهو ته B
آپريشن جي وقت واري ٿريڊ کي هميشه انهن تبديلين جي خبر هوندي جيڪا آپريشن دوران 2
ٿريڊ ۾ ڪئي وئي آهي. . اچو ته انهن کي ڏسو. А
1
ضابطو 1.
هڪ ميوٽڪس کي جاري ڪرڻ کان اڳ ٿئي ٿو ان کان اڳ جو ٻيو سلسلو ساڳيو مانيٽر حاصل ڪري. خير، هتي هر شيء واضح لڳي. جيڪڏهن ڪنهن شئي يا ڪلاس جو ميوٽڪس هڪ ٿريڊ ذريعي حاصل ڪيو وڃي، مثال طور، هڪ ٿريڊА
، ٻيو ٿريڊ (thread B
) هڪ ئي وقت ان کي حاصل نٿو ڪري سگهي. توهان کي انتظار ڪرڻو پوندو جيستائين ميوٽڪس جاري ڪيو وڃي.
ضابطو 2.
طريقيڪارThread.start()
کان اڳ ٿئي ٿو Thread.run()
. ڪجھ به نه پيچيده. توھان اڳ ۾ ئي ڄاڻو ٿا: ڪوڊ جي اندر جي طريقي سان عمل ڪرڻ شروع ڪرڻ لاء run()
، توھان کي سڏڻ جي ضرورت آھي ٿريڊ تي طريقو start()
. اهو هن جو آهي، ۽ طريقو پاڻ ناهي run()
! اهو قاعدو يقيني بڻائي ٿو ته Thread.start()
سڀني متغيرن جا قدر جيڪي عمل ڪرڻ کان اڳ مقرر ڪيا ويا آهن ان طريقي جي اندر ظاهر ٿيندا جنهن تي عمل شروع ڪيو ويو آهي run()
.
ضابطو 3.
طريقي جي مڪمل ٿيڻrun()
کان اڳ طريقو ختم ٿئي ٿو join()
. اچو ته اسان جي ٻن وهڪرو ڏانهن واپس وڃو - А
۽ B
. اسان ان طريقي کي سڏيندا آهيون join()
ته جيئن موضوع B
مڪمل ٿيڻ تائين انتظار ڪيو وڃي A
ان جي ڪم ڪرڻ کان اڳ. هن جو مطلب آهي ته اعتراض A جو طريقو run()
يقيني طور تي تمام آخر تائين هلندو. ۽ ڊيٽا ۾ سڀ تبديليون جيڪي run()
ٿريڊ جي طريقي ۾ ٿينديون آهن A
مڪمل طور تي ٿريڊ ۾ نظر اينديون B
جڏهن اهو مڪمل ٿيڻ جو انتظار ڪندو A
۽ پنهنجو ڪم شروع ڪندو.
ضابطو 4.
هڪ volatile variable ڏانهن لکڻ هڪ ئي variable مان پڙهڻ کان اڳ ٿئي ٿو . غير مستحڪم لفظ استعمال ڪندي، اسان کي، حقيقت ۾، هميشه موجوده قيمت حاصل ڪنداسين.long
جيتوڻيڪ ۽ جي صورت ۾ double
، مسئلا جن سان اڳ ۾ بحث ڪيو ويو آهي. جيئن توهان اڳ ۾ ئي سمجهي چڪا آهيو، ڪجهه موضوعن ۾ ڪيل تبديليون هميشه ٻين موضوعن تي نظر نه اينديون آهن. پر، يقينا، اڪثر ڪري حالتون آهن جڏهن اهڙي پروگرام جي رويي اسان کي مناسب نه آهي. اچو ته چئو ته اسان هڪ ٿريڊ ۾ هڪ متغير کي هڪ قدر مقرر ڪيو آهي A
:
int z;
….
z= 555;
جيڪڏهن اسان جو ٿريڊ ڪنسول ڏانهن B
متغير جي قيمت کي پرنٽ ڪرڻ لاءِ ، اهو آساني سان 0 پرنٽ ڪري سگهي ٿو ڇاڪاڻ ته اهو ان جي مقرر ڪيل قيمت بابت نه ٿو ڄاڻي. z
تنهن ڪري، ضابطو 4 اسان کي ضمانت ڏئي ٿو: جيڪڏهن توهان هڪ متغير کي z
volatile قرار ڏيو ٿا، هڪ سلسلي ۾ ان جي قدرن ۾ تبديليون هميشه ٻئي سلسلي ۾ نظر اينديون. جيڪڏهن اسان لفظ volatile کي پوئين ڪوڊ ۾ شامل ڪريون ٿا...
volatile int z;
….
z= 555;
... اها صورتحال جنهن ۾ وهڪرو B
0 ڪنسول ڏانهن نڪرندو خارج ٿيل آهي. volatile variables ڏانهن لکڻ انهن مان پڙهڻ کان اڳ ٿئي ٿو.
GO TO FULL VERSION