سلام اسان ملٽي ٿريڊنگ جو مطالعو جاري رکون ٿا. اڄ اسان کي معلوم ٿيندو
جڏهن اسان
volatile
لفظ ۽ yield()
طريقو. اچو ته اندر وڃون :)
غير مستحڪم لفظ
جڏهن multithreaded ايپليڪيشنون ٺاهڻ، اسان کي ٻه سنگين مسئلن ۾ هلائي سگهون ٿا. پهريون، جڏهن هڪ ملٽي ٿريڊ ايپليڪيشن هلندي آهي، ته مختلف ٿريڊس ڪيش ڪري سگھن ٿيون متغيرن جي قدرن کي (اسان اڳي ئي ان بابت سبق ۾ ڳالهايو آهي 'استعمال volatile' ). توهان کي اها صورتحال ٿي سگهي ٿي جتي هڪ ٿريڊ هڪ متغير جي قيمت کي تبديل ڪري ٿو، پر هڪ ٻئي ٿريڊ ۾ تبديلي نظر نه ايندي آهي، ڇاڪاڻ ته اهو متغير جي ڪيش ڪيل ڪاپي سان ڪم ڪري رهيو آهي. قدرتي طور، نتيجا سنجيده ٿي سگهي ٿو. فرض ڪريو ته اهو صرف ڪو پراڻو متغير نه آهي، بلڪه توهان جي بئنڪ اڪائونٽ بيلنس، جيڪو اوچتو بي ترتيبيءَ سان مٿي ۽ هيٺ ڪرڻ شروع ڪري ٿو :) اهو مزو نٿو لڳي، صحيح؟ ٻيو، جاوا ۾، سڀني ابتدائي قسمن کي پڙهڻ ۽ لکڻ لاء آپريشن، سواءlong
۽ double
، ايٽمي آهن. مثال طور، جيڪڏهن توهان int
هڪ ٿريڊ تي هڪ متغير جي قيمت کي تبديل ڪريو ٿا، ۽ ٻئي ٿريڊ تي توهان متغير جي قيمت کي پڙهو ٿا، توهان کي يا ته ان جي پراڻي قيمت يا نئين قيمت حاصل ٿيندي، يعني اها قيمت جيڪا تبديلي جي نتيجي ۾ آئي. سلسلي 1 ۾. ڪو به 'وچولي قدر' نه آهي. long
بهرحال، اهو s ۽ double
s سان ڪم نٿو ڪري . ڇو؟ ڪراس پليٽ فارم سپورٽ جي ڪري. شروعاتي سطحن تي ياد رکو ته اسان چيو ته جاوا جو رهنمائي وارو اصول آهي 'هڪ ڀيرو لکو، ڪٿي به هلو'؟ يعني ڪراس پليٽ فارم سپورٽ. ٻين لفظن ۾، جاوا ايپليڪيشن سڀني قسمن جي مختلف پليٽ فارمن تي هلندو آهي. مثال طور، ونڊوز آپريٽنگ سسٽم تي، Linux يا MacOS جا مختلف ورجن. اهو انهن سڀني تي بغير ڪنهن رڪاوٽ جي هلندو. هڪ 64 بٽ ۾ وزن، long
۽ double
جاوا ۾ 'سڀ کان ڳري' پرائمري آهن. ۽ ڪجهه 32-bit پليٽ فارم صرف 64-bit متغير جي ايٽمي پڙهڻ ۽ لکڻ تي عمل نه ڪندا آهن. اهڙا متغير پڙهيا ۽ لکيا ويندا آهن ٻن عملن ۾. پهرين، پهرين 32 بٽ متغير ڏانهن لکيل آهن، ۽ پوء ٻيا 32 بٽ لکيل آهن. نتيجي طور، هڪ مسئلو پيدا ٿي سگهي ٿو. ھڪڙو ٿريڊ ڪجھ 64-bit قدر ھڪڙي X
متغير ڏانھن لکندو آھي ۽ ائين ٻن عملن ۾ ڪندو آھي. ساڳئي وقت، هڪ سيڪنڊ ٿريڊ ڪوشش ڪري ٿو ته متغير جي قدر پڙهڻ ۽ ائين ڪري ٿو انهن ٻن عملن جي وچ ۾ - جڏهن ته پهرين 32 بٽ لکيا ويا آهن، پر ٻئي 32 بٽ نه آهن. نتيجي طور، اهو هڪ وچولي، غلط قدر پڙهي ٿو، ۽ اسان وٽ هڪ بگ آهي. مثال طور، جيڪڏهن اهڙي پليٽ فارم تي اسان نمبر لکڻ جي ڪوشش ڪريون ٿا 9223372036854775809 تي هڪ ويريبل، اهو 64 بٽس تي قبضو ڪندو. بائنري فارم ۾، اهو هن طرح نظر اچي ٿو: 1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 . پهرين تي، اهو پهريون 32 بٽس ( 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 . ۽ ٻيو ٿريڊ انهن عملن جي وچ ۾ ويڙهجي سگهي ٿو، متغير جي وچولي قدر (100000000000000000000000000000000000000000000000000000000000000000000000000000)، جيڪي پهريان 32 بٽ آهن جيڪي اڳ ۾ ئي لکيا ويا آهن. ڊيسيمل سسٽم ۾، هي انگ 2,147,483,648 آهي. ٻين لفظن ۾، اسان صرف نمبر 9223372036854775809 کي هڪ متغير ڏانهن لکڻ چاهيون ٿا، پر حقيقت اها آهي ته اهو آپريشن ڪجهه پليٽ فارمن تي ايٽمي نه آهي، اسان وٽ برائي نمبر 2,147,483,648 آهي، جيڪو هاڻي کان ٻاهر آيو آهي ۽ اڻڄاتل اثر ٿيندو. پروگرام. ٻئي ٿريڊ ۾ لکڻ جي مڪمل ٿيڻ کان اڳ ئي variable جي قيمت پڙهي، يعني ٿريڊ پهرين 32 بِٽ ڏٺي، پر ٻئي 32 بِٽ نه ڏٺي. يقينن، اهي مسئلا ڪالهه پيدا نه ٿيا. جاوا انهن کي هڪ واحد لفظ سان حل ڪري ٿو: volatile
. جيڪڏهن اسان volatile
لفظ استعمال ڪندا آهيون جڏهن اسان جي پروگرام ۾ ڪجهه متغير جو اعلان ڪندا آهيون…
public class Main {
public volatile long x = 2222222222222222222L;
public static void main(String[] args) {
}
}
... مطلب ته:
- اهو هميشه ايٽمي طور تي پڙهيو ۽ لکيو ويندو. جيتوڻيڪ اهو هڪ 64-bit
double
ياlong
. - جاوا مشين ان کي ڪيش نه ڪندي. تنهن ڪري توهان وٽ اهڙي صورتحال نه هوندي جتي 10 موضوع پنهنجن مقامي ڪاپي سان ڪم ڪري رهيا آهن.
پيداوار () جو طريقو
اسان اڳ ۾ ئيThread
ڪلاس جي ڪيترن ئي طريقن جو جائزو ورتو آهي، پر هڪ اهم آهي جيڪو توهان لاءِ نئون هوندو. اهو yield()
طريقو آهي . ۽ اهو ئي ڪري ٿو جيڪو ان جي نالي جو مطلب آهي! 
yield
هڪ سلسلي تي طريقي کي سڏيندا آهيون، اهو اصل ۾ ٻين موضوعن سان ڳالهائيندو آهي: 'اي، دوستو. مون کي ڪٿي به وڃڻ لاءِ ڪا خاص تڪڙ نه آهي، تنهن ڪري جيڪڏهن اهو ضروري آهي ته توهان مان ڪنهن لاءِ پروسيسر وقت حاصل ڪرڻ، ان کي وٺو - مان انتظار ڪري سگهان ٿو. هتي هڪ سادي مثال آهي ته اهو ڪيئن ڪم ڪري ٿو:
public class ThreadExample extends Thread {
public ThreadExample() {
this.start();
}
public void run() {
System.out.println(Thread.currentThread().getName() + " yields its place 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
پنهنجو ڪم مڪمل ڪري ٿو ۽ پوءِ ٿريڊ شيڊيولر پنهنجو ڪوآرڊينيشن جاري رکي ٿو: 'ٺيڪ آهي، Thread-1
ختم ٿي ويو. ڇا اسان جي قطار ۾ ڪو ٻيو آهي؟'. ٿريڊ-0 قطار ۾ آهي: ان جي جاءِ اڳي ئي حاصل ڪئي Thread-1
. اهو هاڻي پنهنجو موڙ حاصل ڪري ٿو ۽ مڪمل ٿيڻ تي هلندو آهي. پوءِ شيڊيولر ٿريڊز کي همٿائڻ ختم ڪري ٿو: 'ٺيڪ آهي، Thread-2
توهان ٻين موضوعن تي حاصل ڪيو، ۽ اهي سڀ هاڻي ٿي ويا آهن. توهان حاصل ڪرڻ وارا آخري هئا، تنهنڪري هاڻي توهان جي باري آهي. ان کان پوء Thread-2
مڪمل ٿيڻ تي هلندو آهي. ڪنسول آئوٽ پٽ هن طرح نظر ايندو: Thread-0 پنهنجي جاءِ ڏئي ٿو ٻين کي Thread-1 yields ان جي جاءِ ٻين کي Thread-2 yields ان جي جاءِ ٻين ڏانهن Thread-1 ختم ٿي چڪو آهي. Thread-0 تي عملدرآمد ختم ٿي چڪو آهي. Thread-2 تي عملدرآمد ختم ٿي چڪو آهي. يقينن، ٿريڊ شيڊيولر شايد موضوعن کي مختلف ترتيب سان شروع ڪري سگھي ٿو (مثال طور، 0-1-2 جي بدران 2-1-0)، پر اصول ساڳيو رهي ٿو.
ٿئي ٿو- ضابطن کان اڳ
آخري شيء جنهن تي اسان اڄ رابطو ڪنداسين اهو تصور آهي ' اڳ ۾ ٿئي ٿو '. جئين توهان اڳ ۾ ئي ڄاڻو ٿا، جاوا ۾ ٿريڊ شيڊولر انهن ڪمن کي انجام ڏيڻ لاءِ ٿريڊز کي وقت ۽ وسيلا مختص ڪرڻ ۾ شامل ڪم جو وڏو حصو انجام ڏئي ٿو. توهان پڻ بار بار ڏٺو آهي ته ڪيئن موضوعن تي عمل ڪيو وڃي ٿو بي ترتيب ترتيب جنهن جي اڳڪٿي ڪرڻ عام طور تي ناممڪن آهي. ۽ عام طور تي، 'ترتيباتي' پروگرامنگ کان پوءِ جيڪو اسان اڳ ڪيو هو، ملٽي ٿريڊ پروگرامنگ ڪجهه بي ترتيب وانگر نظر اچي ٿي. توهان اڳ ۾ ئي يقين ڪيو آهي ته توهان ڪيترن ئي طريقن جي ميزباني کي استعمال ڪري سگهو ٿا هڪ گھڻائي واري پروگرام جي وهڪري کي ڪنٽرول ڪرڻ لاء. پر جاوا ۾ ملٽي ٿريڊنگ ۾ ھڪڙو وڌيڪ ستون آھي - 4 ' ٿيڻ کان اڳ ' ضابطا. انهن ضابطن کي سمجهڻ بلڪل سادو آهي. تصور ڪريو ته اسان وٽ ٻه سلسلا آهن -A
۽ B
. انهن سلسلي مان هر هڪ آپريشن ڪري سگهي ٿو 1
۽ 2
. هر قاعدي ۾، جڏهن اسان چئون ٿا ' A ٿئي ٿو-B کان اڳA
'، اسان جو مطلب اهو آهي ته آپريشن کان اڳ ٿريڊ ۾ ڪيل سموريون تبديليون 1
۽ ان آپريشن جي نتيجي ۾ ٿيندڙ تبديليون ٿريڊ ۾ نظر اينديون آهن B
جڏهن آپريشن 2
ڪيو ويندو آهي ۽ ان کان پوءِ. هر قاعدو ضمانت ڏئي ٿو ته جڏهن توهان هڪ ملٽي ٿريڊ پروگرام لکندا آهيو، ڪجهه واقعا ٻين کان 100 سيڪڙو اڳ واقع ٿيندا، ۽ اهو ته آپريشن جي سلسلي ۾ هميشه انهن تبديلين کان آگاهه هوندو جيڪي ٿريڊ 2
آپريشن دوران ڪيون . اچو ته انهن جو جائزو وٺون. B
A
1
ضابطو 1.
هڪ ميوٽڪس جاري ڪرڻ کان اڳ ٿئي ٿو ساڳئي مانيٽر ٻئي سلسلي طرفان حاصل ڪيو وڃي. منهنجو خيال آهي ته توهان هتي سڀ ڪجهه سمجهي رهيا آهيو. جيڪڏهن ڪنهن شئي يا ڪلاس جو ميوٽڪس هڪ ٿريڊ ذريعي حاصل ڪيو وڃي، مثال طور، thread ذريعيA
، ٻيو ڌاڳو (thread B
) ان کي ساڳئي وقت حاصل نٿو ڪري سگهي. اهو انتظار ڪرڻ گهرجي جيستائين ميوٽڪس جاري ڪيو وڃي.
ضابطو 2.
طريقو اڳ ۾ ٿيندوThread.start()
آهي . ٻيهر، هتي ڪجھ به ڏکيو ناهي. توھان اڳ ۾ ئي ڄاڻو ٿا ته طريقي جي اندر ڪوڊ کي هلائڻ شروع ڪرڻ لاء ، توھان کي سڏڻ گھرجي ٿريڊ تي طريقو. خاص طور تي، شروعات جو طريقو، نه خود طريقو! اهو قاعدو يقيني بڻائي ٿو ته سڀني متغيرن جا قدر جيڪي اڳ ۾ مقرر ڪيا ويا آهن، هڪ ڀيرو شروع ٿيندي طريقي جي اندر نظر اينديون . Thread.run()
run()
start()
run()
Thread.start()
run()
ضابطو 3.
run()
طريقي جي پڇاڙي طريقي جي واپسي کان اڳ ٿيندي آهيjoin()
. اچو ته اسان جي ٻن موضوعن ڏانهن موٽون: A
۽ B
. اسان ان join()
طريقي کي سڏيندا آهيون ته جيئن ٿريڊ ان جي ڪم ڪرڻ کان اڳ B
ٿريڊ جي مڪمل ٿيڻ جو انتظار ڪرڻ جي ضمانت آهي . A
هن جو مطلب آهي ته A اعتراض جو run()
طريقو تمام آخر تائين هلائڻ جي ضمانت آهي. ۽ ڊيٽا ۾ سڀ تبديليون جيڪي run()
ٿريڊ جي طريقي ۾ ٿينديون A
آهن، سو سيڪڙو ضمانت آهي ته ٿريڊ ۾ نظر اينديون B
هڪ دفعو اهو مڪمل ٿيڻ کان پوءِ ٿريڊ A
جي ڪم جي مڪمل ٿيڻ جي انتظار ۾ ته جيئن اهو پنهنجو ڪم شروع ڪري سگهي.
ضابطو 4.
هڪvolatile
متغير ڏانهن لکڻ ان ساڳئي متغير مان پڙهڻ کان اڳ ٿئي ٿو . جڏهن اسان volatile
لفظ استعمال ڪندا آهيون، اسان اصل ۾ هميشه موجوده قيمت حاصل ڪندا آهيون. جيتوڻيڪ هڪ long
يا سان double
(اسان اڳ ۾ ئي مسئلن بابت ڳالهايو جيڪي هتي ٿي سگهن ٿيون). جيئن توهان اڳ ۾ ئي سمجهي چڪا آهيو، ڪجهه موضوعن تي ڪيل تبديليون هميشه ٻين موضوعن تي نظر نه اينديون آهن. پر، يقينا، اتي تمام گهڻيون حالتون آهن جتي اهڙي رويي اسان کي مناسب ناهي. فرض ڪريو ته اسان ٿريڊ تي هڪ متغير جي قيمت مقرر ڪريون ٿا A
:
int z;
….
z = 555;
جيڪڏهن اسان جي سلسلي کي ڪنسول تي متغير B
جي قيمت ڏيکارڻ گهرجي ، اهو آساني سان 0 ڏيکاري سگهي ٿو، ڇاڪاڻ ته اهو مقرر ڪيل قيمت بابت نه ڄاڻندو آهي. z
پر ضابطو 4 ضمانت ڏئي ٿو ته جيڪڏهن اسان z
متغير کي اعلان ڪريون ٿا volatile
، پوء هڪ ٿريڊ تي ان جي قيمت ۾ تبديليون هميشه ٻئي ٿريڊ تي نظر اينديون. جيڪڏهن اسان لفظ کي volatile
اڳئين ڪوڊ ۾ شامل ڪيو ...
volatile int z;
….
z = 555;
...پوءِ اسان ان صورتحال کي روڪيون ٿا جتي ٿريڊ B
0 ڏيکاري سگھي ٿي. volatile
متغيرن تي لکڻ انھن مان پڙھڻ کان اڳ ٿئي ٿو.
GO TO FULL VERSION