CodeGym /جاوا بلاگ /Random-UR /دھاگوں کا انتظام کرنا۔ غیر مستحکم مطلوبہ الفاظ اور yield(...
John Squirrels
سطح
San Francisco

دھاگوں کا انتظام کرنا۔ غیر مستحکم مطلوبہ الفاظ اور yield() طریقہ

گروپ میں شائع ہوا۔
ہائے! ہم ملٹی تھریڈنگ کا اپنا مطالعہ جاری رکھتے ہیں۔ volatileآج ہم کلیدی لفظ اور طریقہ جانیں گے yield()۔ آئیے اس میں غوطہ لگائیں :)

غیر مستحکم مطلوبہ لفظ

ملٹی تھریڈ ایپلی کیشنز بناتے وقت، ہم دو سنگین مسائل کا شکار ہو سکتے ہیں۔ سب سے پہلے، جب ایک ملٹی تھریڈ ایپلیکیشن چل رہی ہوتی ہے، تو مختلف تھریڈز متغیرات کی قدروں کو کیش کر سکتے ہیں (ہم نے پہلے ہی اس کے بارے میں 'استعمال کرنا' کے عنوان سے سبق میں بات کی ہے )۔ آپ کو ایسی صورت حال ہو سکتی ہے جہاں ایک تھریڈ متغیر کی قدر کو تبدیل کرتا ہے، لیکن دوسرا تھریڈ تبدیلی نہیں دیکھتا، کیونکہ یہ متغیر کی اپنی کیش شدہ کاپی کے ساتھ کام کر رہا ہے۔ قدرتی طور پر، نتائج سنگین ہو سکتے ہیں. فرض کریں کہ یہ صرف کوئی پرانا متغیر نہیں ہے بلکہ آپ کے بینک اکاؤنٹ کا بیلنس ہے، جو اچانک تصادفی طور پر اوپر نیچے ہونا شروع ہو جاتا ہے :) یہ مزے کی طرح نہیں لگتا، ٹھیک ہے؟ دوسرا، جاوا میں، تمام قدیم اقسام کو پڑھنے اور لکھنے کے آپریشنز، سوائے longاور double، جوہری ہیں۔ ٹھیک ہے، مثال کے طور پر، اگر آپ ایک تھریڈ پر کسی متغیر کی قدر کو تبدیل کرتے ہیں int، اور دوسرے تھریڈ پر آپ متغیر کی قدر پڑھتے ہیں، تو آپ کو یا تو اس کی پرانی ویلیو ملے گی یا نئی، یعنی وہ قدر جو تبدیلی کے نتیجے میں ہوئی ہے۔ تھریڈ 1 میں۔ کوئی 'انٹرمیڈیٹ ویلیوز' نہیں ہیں۔ longتاہم، یہ s اور doubles کے ساتھ کام نہیں کرتا ہے ۔ کیوں؟ کراس پلیٹ فارم سپورٹ کی وجہ سے۔ ابتدائی سطحوں پر یاد رکھیں کہ ہم نے کہا تھا کہ جاوا کا رہنما اصول ہے 'ایک بار لکھیں، کہیں بھی چلائیں'؟ اس کا مطلب ہے کراس پلیٹ فارم سپورٹ۔ دوسرے الفاظ میں، جاوا ایپلیکیشن ہر طرح کے مختلف پلیٹ فارمز پر چلتی ہے۔ مثال کے طور پر، ونڈوز آپریٹنگ سسٹم پر، لینکس یا میک او ایس کے مختلف ورژن۔ یہ ان سب پر بغیر کسی رکاوٹ کے چلے گا۔ 64 بٹس میں وزن، longاور doubleجاوا میں 'سب سے بھاری' قدیم ہیں۔ اور کچھ 32 بٹ پلیٹ فارم صرف 64 بٹ متغیرات کی ایٹم ریڈنگ اور رائٹنگ کو لاگو نہیں کرتے ہیں۔ اس طرح کے متغیرات کو دو آپریشنز میں پڑھا اور لکھا جاتا ہے۔ پہلے، پہلے 32 بٹس کو متغیر پر لکھا جاتا ہے، اور پھر مزید 32 بٹس لکھے جاتے ہیں۔ نتیجے کے طور پر، ایک مسئلہ پیدا ہوسکتا ہے. ایک تھریڈ متغیر پر کچھ 64 بٹ ویلیو لکھتا ہے Xاور ایسا دو آپریشنز میں کرتا ہے۔ ایک ہی وقت میں، دوسرا تھریڈ متغیر کی قدر کو پڑھنے کی کوشش کرتا ہے اور ان دو آپریشنز کے درمیان ایسا کرتا ہے - جب پہلے 32 بٹس لکھے گئے ہوں، لیکن دوسرے 32 بٹس میں نہیں ہے۔ نتیجے کے طور پر، یہ ایک انٹرمیڈیٹ، غلط قدر پڑھتا ہے، اور ہمارے پاس ایک بگ ہے۔ مثال کے طور پر، اگر اس طرح کے پلیٹ فارم پر ہم نمبر کو 9223372036854775809 پر متغیر پر لکھنے کی کوشش کرتے ہیں، تو یہ 64 بٹس پر قبضہ کرے گا۔ بائنری شکل میں، یہ اس طرح نظر آتا ہے: 1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 پہلا تھریڈ متغیر پر نمبر لکھنا شروع کرتا ہے۔ سب سے پہلے، یہ پہلے 32 بٹس (1000000000000000000000000000000000000000000) اور پھر دوسرے 32 بٹس (000000000000000000000000000001) لکھتا ہے۔ اور دوسرا دھاگہ ان آپریشنز کے درمیان ویجڈ ہو سکتا ہے، متغیر کی انٹرمیڈیٹ ویلیو (10000000000000000000000000000000000000000000000000000000000000000000000000000) جو کہ پہلے 32 بٹس ہیں جو پہلے ہی لکھے جا چکے ہیں۔ اعشاریہ نظام میں یہ تعداد 2,147,483,648 ہے۔ دوسرے لفظوں میں، ہم صرف ایک متغیر پر نمبر 9223372036854775809 لکھنا چاہتے تھے، لیکن اس حقیقت کی وجہ سے کہ یہ آپریشن کچھ پلیٹ فارمز پر ایٹم نہیں ہے، ہمارے پاس برائی نمبر 2,147,483,648 ہے، جو کہیں سے نہیں نکلا اور اس کا نامعلوم اثر پڑے گا۔ پروگرام دوسرا تھریڈ لکھے جانے سے پہلے صرف متغیر کی قدر کو پڑھتا ہے، یعنی تھریڈ نے پہلے 32 بٹس دیکھے، لیکن دوسرے 32 بٹس کو نہیں۔ یقیناً یہ مسائل کل پیدا نہیں ہوئے۔ جاوا انہیں ایک کلیدی لفظ کے ساتھ حل کرتا ہے: volatile. اگر ہم volatileاپنے پروگرام میں کچھ متغیر کا اعلان کرتے وقت کلیدی لفظ استعمال کرتے ہیں…
public class Main {

   public volatile long x = 2222222222222222222L;

   public static void main(String[] args) {

   }
}
…اس کا مطلب ہے کہ:
  1. یہ ہمیشہ ایٹمی طور پر پڑھا اور لکھا جائے گا۔ یہاں تک کہ اگر یہ 64 بٹ doubleیا long.
  2. جاوا مشین اسے کیش نہیں کرے گی۔ لہذا آپ کو ایسی صورتحال نہیں ہوگی جہاں 10 تھریڈز اپنی مقامی کاپیوں کے ساتھ کام کر رہے ہوں۔
اس طرح دو انتہائی سنگین مسائل صرف ایک لفظ سے حل ہو جاتے ہیں :)

پیداوار () طریقہ

ہم پہلے ہی Threadکلاس کے بہت سے طریقوں کا جائزہ لے چکے ہیں، لیکن ایک اہم طریقہ ہے جو آپ کے لیے نیا ہوگا۔ یہ yield()طریقہ ہے ۔ اور یہ بالکل وہی کرتا ہے جو اس کے نام کا مطلب ہے! دھاگوں کا انتظام کرنا۔  غیر مستحکم مطلوبہ الفاظ اور پیداوار () طریقہ - 2جب ہم 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اور 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 اپنی جگہ دوسروں کو دیتا ہے 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% وقت پر پیش آئیں گے، اور یہ کہ آپریشن کے وقت 2تھریڈ کے دوران ہونے Bوالی تبدیلیوں سے ہمیشہ آگاہ رہیں گے ۔ آئیے ان کا جائزہ لیتے ہیں۔ A1

اصول 1۔

ایک mutex کو جاری کرنا اس سے پہلے ہوتا ہے جب ایک ہی مانیٹر دوسرے دھاگے کے ذریعہ حاصل کیا جائے۔ مجھے لگتا ہے کہ آپ یہاں سب کچھ سمجھتے ہیں۔ اگر کسی شے یا کلاس کا mutex ایک دھاگے کے ذریعے حاصل کیا جاتا ہے۔، مثال کے طور پر، thread کے ذریعے، Aدوسرا دھاگہ (thread B) اسے ایک ہی وقت میں حاصل نہیں کر سکتا۔ اسے mutex کے جاری ہونے تک انتظار کرنا ہوگا۔

اصول 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;
...پھر ہم اس صورتحال کو روکتے ہیں جہاں تھریڈ B0 ظاہر کر سکتا ہے۔ volatileمتغیرات کو لکھنا ان سے پڑھنے سے پہلے ہوتا ہے۔
تبصرے
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION