CodeGym /مدونة جافا /Random-AR /أفضل معًا: Java وفئة Thread. الجزء الأول – خيوط التنفيذ
John Squirrels
مستوى
San Francisco

أفضل معًا: Java وفئة Thread. الجزء الأول – خيوط التنفيذ

نشرت في المجموعة

مقدمة

تم إنشاء خاصية Multithreading في لغة Java منذ البداية. لذلك، دعونا نلقي نظرة سريعة على هذا الشيء الذي يسمى تعدد العمليات. أفضل معًا: Java وفئة Thread.  الجزء الأول – خيوط التنفيذ – 1نحن نأخذ الدرس الرسمي من Oracle كنقطة مرجعية: " الدرس: تطبيق "Hello World! ". سنقوم بتغيير رمز برنامج Hello World الخاص بنا قليلاً كما يلي:
class HelloWorldApp {
    public static void main(String[] args) {
        System.out.println("Hello, " + args[0]);
    }
}
argsعبارة عن مجموعة من معلمات الإدخال التي يتم تمريرها عند بدء تشغيل البرنامج. احفظ هذا الرمز في ملف باسم يطابق اسم الفئة وله الامتداد .java. قم بتجميعها باستخدام الأداة المساعدة javac : javac HelloWorldApp.java. بعد ذلك، نقوم بتشغيل الكود الخاص بنا باستخدام بعض المعلمات، على سبيل المثال، "Roger": java HelloWorldApp Roger أفضل معًا: Java وفئة Thread.  الجزء الأول – خيوط التنفيذ – 2يحتوي الكود الخاص بنا حاليًا على عيب خطير. إذا لم تقم بتمرير أي وسيطة (على سبيل المثال، قم بتنفيذ "java HelloWorldApp" فقط)، فسنحصل على خطأ:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
        at HelloWorldApp.main(HelloWorldApp.java:3)
حدث استثناء (أي خطأ) في مؤشر الترابط المسمى "الرئيسي". لذلك، جافا لديها المواضيع؟ هذا هو المكان الذي تبدأ رحلتنا.

جافا والخيوط

لفهم ما هو الخيط، عليك أن تفهم كيف يبدأ برنامج Java. دعونا نغير الكود الخاص بنا كما يلي:
class HelloWorldApp {
    public static void main(String[] args) {
		while (true) {
			// Do nothing
		}
	}
}
الآن دعونا نجمعها مرة أخرى مع javac. وللتسهيل عليك، سنقوم بتشغيل كود Java الخاص بنا في نافذة منفصلة. في نظام التشغيل Windows، يمكن القيام بذلك على النحو التالي: start java HelloWorldApp. سنستخدم الآن الأداة المساعدة jps لمعرفة المعلومات التي يمكن أن تخبرنا بها Java: أفضل معًا: Java وفئة Thread.  الجزء الأول – خيوط التنفيذ – 3الرقم الأول هو PID أو معرف العملية. ما هي العملية؟
A process is a combination of code and data sharing a common virtual address space.
من خلال العمليات، يتم عزل البرامج المختلفة عن بعضها البعض أثناء تشغيلها: يستخدم كل تطبيق منطقته الخاصة في الذاكرة دون التدخل في البرامج الأخرى. لمعرفة المزيد، أوصي بقراءة هذا البرنامج التعليمي: العمليات والخيوط . لا يمكن أن توجد العملية بدون مؤشر ترابط، لذلك إذا كانت العملية موجودة، فهي تحتوي على مؤشر ترابط واحد على الأقل. ولكن كيف يحدث هذا في جافا؟ عندما نبدأ برنامج Java، يبدأ التنفيذ بالطريقة main. يبدو الأمر كما لو كنا ندخل في البرنامج، لذلك mainتسمى هذه الطريقة الخاصة بنقطة الدخول. يجب أن تكون الطريقة mainدائمًا "فراغًا ثابتًا عامًا"، حتى يتمكن جهاز Java الظاهري (JVM) من البدء في تنفيذ برنامجنا. لمزيد من المعلومات، لماذا تعتبر طريقة Java الرئيسية ثابتة؟ . اتضح أن مشغل Java (java.exe أو javaw.exe) هو تطبيق بسيط بلغة C: فهو يقوم بتحميل ملفات DLL المتنوعة التي تتكون بالفعل من JVM. يقوم مشغل Java بإجراء مجموعة محددة من مكالمات Java Native Interface (JNI). JNI هي آلية لربط عالم جهاز Java الظاهري بعالم C++. لذا، فإن المُشغل ليس JVM نفسه، بل هو آلية لتحميله. إنه يعرف الأوامر الصحيحة التي يجب تنفيذها لبدء تشغيل JVM. إنه يعرف كيفية استخدام مكالمات JNI لإعداد البيئة اللازمة. يتضمن إعداد هذه البيئة إنشاء الموضوع الرئيسي، والذي يسمى "الرئيسي" بالطبع. لتوضيح المواضيع الموجودة في عملية Java بشكل أفضل، نستخدم أداة jvisualvm ، المضمنة مع JDK. بمعرفة معرف عملية ما، يمكننا أن نرى على الفور معلومات حول هذه العملية: jvisualvm --openpid <process id> أفضل معًا: Java وفئة Thread.  الجزء الأول – خيوط التنفيذ – 4ومن المثير للاهتمام أن كل مؤشر ترابط له منطقة منفصلة خاصة به في الذاكرة المخصصة للعملية. تسمى بنية الذاكرة هذه بالمكدس. المكدس يتكون من إطارات. يمثل الإطار تنشيط الأسلوب (استدعاء أسلوب غير مكتمل). يمكن أيضًا تمثيل الإطار باعتباره StackTraceElement (راجع Java API لـ StackTraceElement ). يمكنك العثور على مزيد من المعلومات حول الذاكرة المخصصة لكل موضوع في المناقشة هنا: " كيف تقوم Java (JVM) بتخصيص المكدس لكل موضوع ". إذا نظرت إلى Java API وبحثت عن كلمة "Thread"، ستجد فئة java.lang.Thread . هذه هي الفئة التي تمثل مؤشر ترابط في Java، وسنحتاج إلى العمل معها. أفضل معًا: Java وفئة Thread.  الجزء الأول – خيوط التنفيذ – 5

java.lang.Thread

في Java، يتم تمثيل مؤشر الترابط بواسطة مثيل للفئة java.lang.Thread. يجب أن تفهم على الفور أن مثيلات فئة Thread ليست في حد ذاتها مؤشرات ترابط للتنفيذ. هذا مجرد نوع من واجهة برمجة التطبيقات (API) للسلاسل ذات المستوى المنخفض التي يديرها JVM ونظام التشغيل. عندما نبدأ JVM باستخدام مشغل Java، فإنه يقوم بإنشاء mainسلسلة رسائل تسمى "الرئيسية" وعدد قليل من سلاسل العمليات الأخرى. كما هو مذكور في JavaDoc لفئة الموضوع: When a Java Virtual Machine starts up, there is usually a single non-daemon thread. هناك نوعان من المواضيع: الشياطين وغير الشياطين. خيوط الشيطان هي خيوط خلفية (تدبير منزلي) تقوم ببعض الأعمال في الخلفية. تشير كلمة "الخفي" إلى شيطان ماكسويل. يمكنك معرفة المزيد في مقالة ويكيبيديا هذه . كما هو مذكور في الوثائق، يستمر JVM في تنفيذ البرنامج (العملية) حتى:
  • يتم استدعاء الأسلوب Runtime.exit ()
  • جميع سلاسل الرسائل غير الشيطانية تنتهي من عملها (بدون أخطاء أو مع استثناءات مطروحة)
يترتب على ذلك تفصيل مهم: يمكن إنهاء سلاسل العمليات الخفية في أي وقت. ونتيجة لذلك، لا توجد ضمانات بشأن سلامة بياناتهم. وبناء على ذلك، فإن الخيوط الخفية مناسبة لبعض مهام التدبير المنزلي. على سبيل المثال، تحتوي Java على مؤشر ترابط مسؤول عن معالجة finalize()استدعاءات الأسلوب، أي سلاسل الرسائل المتضمنة في Garbage Collector (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. كل موضوع لديه أيضا الأولوية. يمكنك قراءة المزيد عن الأولويات في هذه المقالة: Java Thread Priority in Multithreading .

إنشاء موضوع

كما هو مذكور في الوثائق، لدينا طريقتان لإنشاء سلسلة رسائل. الطريقة الأولى هي إنشاء فئة فرعية خاصة بك. على سبيل المثال:
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()الطريقة. لا تخلط بين هذه الطرق: إذا قمنا باستدعاء un()طريقة r مباشرة، فلن يتم بدء أي موضوع جديد. إنها start()الطريقة التي تطلب من JVM إنشاء سلسلة رسائل جديدة. هذا الخيار الذي نرث فيه مؤشر الترابط سيئ بالفعل لأننا قمنا بتضمين مؤشر الترابط في التسلسل الهرمي لفئتنا. والعيب الثاني هو أننا بدأنا في انتهاك مبدأ "المسؤولية الفردية". وهذا يعني أن فصلنا مسؤول في نفس الوقت عن التحكم في سلسلة الرسائل وعن بعض المهام التي يتعين تنفيذها في هذا الموضوع. ما هي الطريقة الصحيحة؟ الجواب موجود بنفس run()الطريقة التي نتجاوزها:
public void run() {
	if (target != null) {
		target.run();
	}
}
إليك targetبعض الأشياء java.lang.Runnableالتي يمكننا تمريرها عند إنشاء مثيل لفئة Thread. هذا يعني أنه يمكننا القيام بذلك:
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كانت أيضًا واجهة وظيفية منذ Java 1.8. وهذا يجعل من الممكن كتابة تعليمات برمجية أكثر جمالاً لمهمة سلسلة ما:
public static void main(String[] args) {
	Runnable task = () -> {
		System.out.println("Hello, World!");
	};
	Thread thread = new Thread(task);
	thread.start();
}

خاتمة

آمل أن توضح هذه المناقشة ما هو الخيط، وكيف تظهر الخيوط إلى الوجود، وما هي العمليات الأساسية التي يمكن إجراؤها باستخدام الخيوط. في الجزء التالي ، سنحاول فهم كيفية تفاعل الخيوط مع بعضها البعض واستكشاف دورة حياة الخيوط. أفضل معًا: Java وفئة Thread. الجزء الثاني – التزامن بشكل أفضل معًا: Java وفئة Thread. الجزء الثالث - التفاعل بشكل أفضل معًا: Java وفئة Thread. الجزء الرابع - الأشخاص القابلون للاستدعاء والمستقبل والأصدقاء أفضل معًا: Java وفئة Thread. الجزء الخامس — Executor، ThreadPool، Fork/Join Better Together: Java وفئة Thread. الجزء السادس – أطلق النار بعيدًا!
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION