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

ایک ساتھ بہتر: جاوا اور تھریڈ کلاس۔ حصہ I - عمل درآمد کے سلسلے

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

تعارف

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

class HelloWorldApp {
    public static void main(String[] args) {
        System.out.println("Hello, " + args[0]);
    }
}
argsپروگرام شروع ہونے پر پاس کردہ ان پٹ پیرامیٹرز کی ایک صف ہے۔ اس کوڈ کو کسی ایسے نام کے ساتھ فائل میں محفوظ کریں جو کلاس کے نام سے مماثل ہو اور اس میں توسیع ہو .java۔ javac یوٹیلیٹی کا استعمال کرتے ہوئے اسے مرتب کریں : javac HelloWorldApp.java. پھر، ہم اپنے کوڈ کو کچھ پیرامیٹر کے ساتھ چلاتے ہیں، مثال کے طور پر، "Roger": java HelloWorldApp Roger ایک ساتھ بہتر: جاوا اور تھریڈ کلاس۔  حصہ اول - پھانسی کے سلسلے - 2ہمارے کوڈ میں فی الحال ایک سنگین خامی ہے۔ اگر آپ کوئی دلیل پاس نہیں کرتے ہیں (یعنی صرف "java HelloWorldApp" پر عمل درآمد کریں)، تو ہمیں ایک خرابی ملتی ہے:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
        at HelloWorldApp.main(HelloWorldApp.java:3)
"مین" نامی دھاگے میں ایک استثناء (یعنی ایک غلطی) واقع ہوئی ہے۔ تو، جاوا میں تھریڈز ہیں؟ یہیں سے ہمارا سفر شروع ہوتا ہے۔

جاوا اور تھریڈز

یہ سمجھنے کے لیے کہ تھریڈ کیا ہے، آپ کو یہ سمجھنا ہوگا کہ جاوا پروگرام کیسے شروع ہوتا ہے۔ آئیے اپنا کوڈ اس طرح تبدیل کریں:

class HelloWorldApp {
    public static void main(String[] args) {
		while (true) { 
			// Do nothing
		}
	}
}
اب ہم اسے دوبارہ سے مرتب کرتے ہیں javac۔ سہولت کے لیے، ہم اپنا جاوا کوڈ الگ ونڈو میں چلائیں گے۔ ونڈوز پر، یہ اس طرح کیا جا سکتا ہے: start java HelloWorldApp. اب ہم یہ دیکھنے کے لیے jps یوٹیلیٹی استعمال کریں گے کہ جاوا ہمیں کیا معلومات بتا سکتا ہے: ایک ساتھ بہتر: جاوا اور تھریڈ کلاس۔  حصہ اول - پھانسی کے سلسلے - 3پہلا نمبر PID یا Process ID ہے۔ ایک عمل کیا ہے؟

A process is a combination of code and data sharing a common virtual address space.
عمل کے ساتھ، مختلف پروگرام چلتے ہی ایک دوسرے سے الگ ہو جاتے ہیں: ہر ایپلیکیشن دوسرے پروگراموں میں مداخلت کیے بغیر میموری میں اپنا اپنا علاقہ استعمال کرتی ہے۔ مزید جاننے کے لیے، میں اس ٹیوٹوریل کو پڑھنے کی تجویز کرتا ہوں: Processes and Threads ۔ ایک عمل دھاگے کے بغیر موجود نہیں ہوسکتا، لہذا اگر کوئی عمل موجود ہے، تو اس میں کم از کم ایک دھاگہ ہوتا ہے۔ لیکن یہ جاوا میں کیسے آتا ہے؟ جب ہم جاوا پروگرام شروع کرتے ہیں، تو mainطریقہ سے عمل درآمد شروع ہوتا ہے۔ ایسا لگتا ہے جیسے ہم پروگرام میں قدم رکھ رہے ہیں، اس لیے اس خاص mainطریقہ کو انٹری پوائنٹ کہا جاتا ہے۔ طریقہ mainہمیشہ "عوامی جامد باطل" ہونا چاہیے، تاکہ جاوا ورچوئل مشین (JVM) ہمارے پروگرام پر عمل درآمد شروع کر سکے۔ مزید معلومات کے لیے، جاوا مین طریقہ جامد کیوں ہے؟ . یہ پتہ چلتا ہے کہ جاوا لانچر (java.exe یا javaw.exe) ایک سادہ سی ایپلی کیشن ہے: یہ مختلف DLLs کو لوڈ کرتا ہے جو دراصل JVM پر مشتمل ہوتے ہیں۔ جاوا لانچر Java Native Interface (JNI) کالز کا ایک مخصوص سیٹ بناتا ہے۔ JNI جاوا ورچوئل مشین کی دنیا کو C++ کی دنیا سے جوڑنے کا ایک طریقہ کار ہے۔ لہذا، لانچر خود JVM نہیں ہے، بلکہ اسے لوڈ کرنے کا ایک طریقہ کار ہے۔ یہ JVM کو شروع کرنے کے لیے درست احکامات کو جانتا ہے۔ یہ ضروری ماحول کو ترتیب دینے کے لیے JNI کالز کا استعمال کرنا جانتا ہے۔ اس ماحول کو ترتیب دینے میں بنیادی دھاگہ بنانا شامل ہے، جسے یقیناً "مین" کہا جاتا ہے۔ بہتر طور پر واضح کرنے کے لیے کہ جاوا کے عمل میں کون سے تھریڈز موجود ہیں، ہم jvisualvm ٹول استعمال کرتے ہیں، جو JDK کے ساتھ شامل ہے۔ کسی عمل کے پیڈ کو جاننے کے بعد، ہم فوری طور پر اس عمل کے بارے میں معلومات دیکھ سکتے ہیں: jvisualvm --openpid <process id> ایک ساتھ بہتر: جاوا اور تھریڈ کلاس۔  حصہ اول - پھانسی کے سلسلے - 4دلچسپ بات یہ ہے کہ ہر تھریڈ کا اس عمل کے لیے مختص میموری میں اپنا الگ علاقہ ہوتا ہے۔ اس میموری کی ساخت کو اسٹیک کہا جاتا ہے۔ ایک اسٹیک فریموں پر مشتمل ہوتا ہے۔ ایک فریم ایک طریقہ کار کے فعال ہونے کی نمائندگی کرتا ہے (ایک نامکمل طریقہ کال)۔ ایک فریم کو StackTraceElement کے طور پر بھی دکھایا جا سکتا ہے ( StackTraceElement کے لیے Java API دیکھیں )۔ آپ یہاں بحث میں ہر تھریڈ کے لیے مختص میموری کے بارے میں مزید معلومات حاصل کر سکتے ہیں: " جاوا (JVM) ہر تھریڈ کے لیے اسٹیک کیسے مختص کرتا ہےاگر آپ Java API کو دیکھیں اور لفظ "Thread" تلاش کریں تو آپ کو java.lang.Thread کلاس مل جائے گی۔ یہ وہ کلاس ہے جو جاوا میں تھریڈ کی نمائندگی کرتی ہے، اور ہمیں اس کے ساتھ کام کرنے کی ضرورت ہوگی۔ ایک ساتھ بہتر: جاوا اور تھریڈ کلاس۔  حصہ اول - پھانسی کے سلسلے - 5

java.lang.thread

جاوا میں، ایک تھریڈ کی نمائندگی کلاس کی مثال سے کی جاتی ہے java.lang.Thread۔ آپ کو فوری طور پر سمجھ لینا چاہئے کہ تھریڈ کلاس کی مثالیں خود عمل درآمد کے دھاگے نہیں ہیں۔ یہ JVM اور آپریٹنگ سسٹم کے زیر انتظام نچلے درجے کے تھریڈز کے لیے صرف ایک قسم کا API ہے۔ جب ہم جاوا لانچر کا استعمال کرتے ہوئے JVM کو شروع کرتے ہیں، تو یہ ایک mainدھاگہ بناتا ہے جسے "مین" کہتے ہیں اور کچھ دوسرے ہاؤس کیپنگ تھریڈز۔ جیسا کہ جاوا ڈوک میں تھریڈ کلاس کے لیے کہا گیا ہے: When a Java Virtual Machine starts up, there is usually a single non-daemon thread۔ دھاگوں کی 2 قسمیں ہیں: ڈیمن اور نان ڈیمون۔ ڈیمون تھریڈز پس منظر (ہاؤس کیپنگ) تھریڈز ہیں جو پس منظر میں کچھ کام کرتے ہیں۔ لفظ "ڈیمن" میکسویل کے شیطان سے مراد ہے۔ آپ اس ویکیپیڈیا مضمون میں مزید جان سکتے ہیں ۔ جیسا کہ دستاویزات میں بیان کیا گیا ہے، JVM پروگرام (عمل) کو اس وقت تک انجام دیتا رہتا ہے جب تک:
  • Runtime.exit () طریقہ کہا جاتا ہے۔
  • تمام نان ڈیمون تھریڈز اپنا کام مکمل کر لیتے ہیں (بغیر غلطیوں یا مستثنیات کے)
اس سے ایک اہم تفصیل مندرجہ ذیل ہے: ڈیمون تھریڈز کو کسی بھی وقت ختم کیا جا سکتا ہے۔ نتیجے کے طور پر، ان کے ڈیٹا کی سالمیت کے بارے میں کوئی ضمانت نہیں ہے۔ اس کے مطابق، ڈیمون تھریڈز کچھ گھریلو کاموں کے لیے موزوں ہیں۔ مثال کے طور پر، جاوا میں ایک دھاگہ ہے جو طریقہ کار کی finalize()کالز کے لیے ذمہ دار ہے، یعنی دھاگے جو کہ کوڑا اٹھانے والے (gc) کے ساتھ شامل ہیں۔ ہر تھریڈ ایک گروپ کا حصہ ہے ( ThreadGroup )۔ اور گروہ دوسرے گروہوں کا حصہ ہو سکتے ہیں، ایک مخصوص درجہ بندی یا ڈھانچہ تشکیل دیتے ہیں۔

public static void main(String[] args) {
	Thread currentThread = Thread.currentThread();
	ThreadGroup threadGroup = currentThread.getThreadGroup();
	System.out.println("Thread: " + currentThread.getName());
	System.out.println("Thread Group: " + threadGroup.getName());
	System.out.println("Parent Group: " + threadGroup.getParent().getName());
}
گروپس تھریڈ مینجمنٹ کو ترتیب دیتے ہیں۔ گروپس کے علاوہ، تھریڈز کا اپنا ایک استثنائی ہینڈلر ہوتا ہے۔ ایک مثال پر نظر ڈالیں:

public static void main(String[] args) {
	Thread th = Thread.currentThread();
	th.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
		@Override
		public void uncaughtException(Thread t, Throwable e) {
			System.out.println("An error occurred: " + e.getMessage());
		}
	});
    System.out.println(2/0);
}
صفر سے تقسیم ایک غلطی کا سبب بنے گی جو ہینڈلر کے ذریعہ پکڑا جائے گا۔ اگر آپ اپنے ہینڈلر کی وضاحت نہیں کرتے ہیں، تو JVM پہلے سے طے شدہ ہینڈلر کو طلب کرے گا، جو استثناء کے اسٹیک ٹریس کو StdError میں آؤٹ پٹ کرے گا۔ ہر تھریڈ کی بھی ایک ترجیح ہوتی ہے۔ آپ اس مضمون میں ترجیحات کے بارے میں مزید پڑھ سکتے ہیں: ملٹی تھریڈنگ میں جاوا تھریڈ کی ترجیح ۔

ایک دھاگہ بنانا

جیسا کہ دستاویزات میں بتایا گیا ہے، ہمارے پاس تھریڈ بنانے کے 2 طریقے ہیں۔ پہلا طریقہ یہ ہے کہ آپ اپنا ذیلی طبقہ بنائیں۔ مثال کے طور پر:

public class HelloWorld{
    public static class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("Hello, World!");  
        }
    }
    
    public static void main(String[] args) {
        Thread thread = new MyThread();
        thread.start();
    }
}
جیسا کہ آپ دیکھ سکتے ہیں، ٹاسک کا کام run()طریقہ میں ہوتا ہے، لیکن تھریڈ خود start()طریقہ میں شروع ہوتا ہے۔ ان طریقوں کو الجھن میں نہ ڈالیں: اگر ہم r un()طریقہ کو براہ راست کہتے ہیں، تو کوئی نیا تھریڈ شروع نہیں کیا جائے گا۔ یہ وہ start()طریقہ ہے جو JVM سے ایک نیا تھریڈ بنانے کو کہتا ہے۔ یہ آپشن جہاں ہم تھریڈ کو وراثت میں حاصل کرتے ہیں پہلے سے ہی خراب ہے کیونکہ ہم اپنی کلاس کے درجہ بندی میں تھریڈ کو شامل کر رہے ہیں۔ دوسری خرابی یہ ہے کہ ہم "واحد ذمہ داری" کے اصول کی خلاف ورزی کرنا شروع کر رہے ہیں۔ یعنی ہماری کلاس بیک وقت تھریڈ کو کنٹرول کرنے اور اس تھریڈ میں انجام پانے والے کسی کام کی ذمہ دار ہے۔ صحیح طریقہ کیا ہے؟ جواب اسی run()طریقہ میں پایا جاتا ہے، جسے ہم اوور رائیڈ کرتے ہیں:

public void run() {
	if (target != null) {
		target.run();
	}
}
یہاں، targetکچھ ہے java.lang.Runnable، جسے ہم تھریڈ کلاس کی مثال بناتے وقت پاس کر سکتے ہیں۔ اس کا مطلب ہے کہ ہم یہ کر سکتے ہیں:

public class HelloWorld{
    public static void main(String[] args) {
        Runnable task = new Runnable() {
            public void run() {
                System.out.println("Hello, World!");
            } 
        };
        Thread thread = new Thread(task);
        thread.start();
    }
}
Runnableجاوا 1.8 کے بعد سے ایک فعال انٹرفیس بھی رہا ہے۔ اس سے تھریڈ کے کام کے لیے اور بھی خوبصورت کوڈ لکھنا ممکن ہو جاتا ہے۔

public static void main(String[] args) {
	Runnable task = () -> { 
		System.out.println("Hello, World!");
	};
	Thread thread = new Thread(task);
	thread.start();
}

نتیجہ

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