CodeGym /مدونة جافا /Random-AR /تعدد مؤشرات الترابط في جافا
John Squirrels
مستوى
San Francisco

تعدد مؤشرات الترابط في جافا

نشرت في المجموعة
أهلاً! بداية، تهانينا: لقد وصلت إلى موضوع تعدد مؤشرات الترابط في Java! يعد هذا إنجازًا خطيرًا، لقد قطعت شوطًا طويلًا. لكن جهز نفسك: هذا من أصعب المواضيع في الدورة. ولا يعني ذلك أننا نستخدم فئات معقدة أو الكثير من الأساليب هنا: في الواقع، سنستخدم أقل من عشرين. والأهم من ذلك أنك ستحتاج إلى تغيير طريقة تفكيرك قليلاً. في السابق، تم تنفيذ برامجك بشكل تسلسلي. جاءت بعض أسطر التعليمات البرمجية بعد أخرى، وجاءت بعض الأساليب بعد أخرى، وكان كل شيء واضحًا في الأساس. أولاً، قمنا بحساب شيء ما، ثم عرضنا النتيجة على وحدة التحكم، ثم انتهى البرنامج. لفهم تعدد مؤشرات الترابط، من الأفضل التفكير من حيث التوازي. لنبدأ بشيء بسيط جدًا :) تخيل أن عائلتك تنتقل من منزل إلى آخر. سيكون جمع كل كتبك جزءًا مهمًا من هذه الخطوة. لقد جمعت الكثير من الكتب، وتحتاج إلى وضعها في الصناديق. حاليًا، أنت الوحيد المتاح. أمي تحضر الطعام، والأخ يحزم الملابس، وذهبت الأخت إلى المتجر. بمفردك، يمكنك إدارة الأمور بطريقة أو بأخرى. عاجلاً أم آجلاً، ستكمل المهمة بنفسك، لكن الأمر سيستغرق الكثير من الوقت. ومع ذلك، ستعود أختك من المتجر خلال 20 دقيقة، وليس لديها أي شيء آخر لتفعله. حتى تتمكن من الانضمام إليك. المهمة لم تتغير: ضع الكتب في الصناديق. ولكن يتم تنفيذه بسرعة مضاعفة. لماذا؟ لأن العمل يحدث بالتوازي. يقوم "خيطان" مختلفان (أنت وأختك) بتنفيذ نفس المهمة في وقت واحد. وإذا لم يتغير شيء، فسيكون هناك فارق زمني كبير مقارنة بالموقف الذي تفعل فيه كل شيء بنفسك. إذا أنهى أخي وظيفته قريبًا، فيمكنه مساعدتك وستسير الأمور بشكل أسرع.

تم حل المشاكل عن طريق تعدد الخيوط

لقد تم اختراع تقنية Multithreading في الواقع لتحقيق هدفين مهمين:
  1. القيام بعدة أشياء في نفس الوقت.

    في المثال أعلاه، قامت سلاسل مختلفة (أفراد الأسرة) بتنفيذ عدة إجراءات بالتوازي: غسل الأطباق، والذهاب إلى المتجر، وتعبئة الأشياء.

    يمكننا أن نقدم مثالاً أكثر ارتباطًا بالبرمجة. لنفترض أن لديك برنامجًا مزودًا بواجهة مستخدم. عند النقر فوق "متابعة" في البرنامج، يجب أن تتم بعض الحسابات ويجب أن يرى المستخدم الشاشة التالية. إذا تم تنفيذ هذه الإجراءات بشكل تسلسلي، فسيتم تعليق البرنامج بعد أن يقوم المستخدم بالنقر فوق الزر "متابعة". سيظهر للمستخدم شاشة الزر "متابعة" حتى يقوم البرنامج بإجراء جميع الحسابات الداخلية ويصل إلى الجزء الذي يتم فيه تحديث واجهة المستخدم.

    حسنًا، أعتقد أننا سننتظر بضع دقائق!

    تعدد مؤشرات الترابط في جافا: ما هو، فوائده والمزالق الشائعة - 3

    أو يمكننا إعادة صياغة برنامجنا، أو، كما يقول المبرمجون، "موازيته". لنجري حساباتنا على موضوع واحد ونرسم واجهة المستخدم على موضوع آخر. تمتلك معظم أجهزة الكمبيوتر موارد كافية للقيام بذلك. إذا سلكنا هذا الطريق، فلن يتجمد البرنامج وسيتحرك المستخدم بسلاسة بين الشاشات دون القلق بشأن ما يحدث بالداخل. ولا يتدخل أحدهما في الآخر :)

  2. إجراء العمليات الحسابية بسرعة أكبر.

    كل شيء أبسط بكثير هنا. إذا كان معالجنا يحتوي على العديد من النوى، ومعظم المعالجات اليوم تفعل ذلك، فيمكن للعديد من النوى التعامل مع قائمة المهام بالتوازي. من الواضح أنه إذا كنا بحاجة إلى تنفيذ 1000 مهمة وتستغرق كل منها ثانية واحدة، فيمكن لنواة واحدة إنهاء القائمة في 1000 ثانية، ونواتين في 500 ثانية، وثلاثة في ما يزيد قليلاً عن 333 ثانية، وما إلى ذلك.

ولكن كما قرأت بالفعل في هذا الدرس، فإن أنظمة اليوم ذكية جدًا، وحتى على نواة حوسبة واحدة تكون قادرة على تحقيق التوازي، أو بالأحرى التوازي الزائف، حيث يتم تنفيذ المهام بالتناوب. دعنا ننتقل من العموميات إلى التفاصيل ونتعرف على الفئة الأكثر أهمية في مكتبة Java multithreading - java.lang.Thread. بالمعنى الدقيق للكلمة، يتم تمثيل سلاسل رسائل Java بمثيلات فئة Thread . هذا يعني أنه لإنشاء 10 سلاسل رسائل وتشغيلها، فإنك تحتاج إلى 10 مثيلات من هذه الفئة. لنكتب أبسط مثال:
public class MyFirstThread extends Thread {

   @Override
   public void run() {
       System.out.println("I'm Thread! My name is " + getName());
   }
}
لإنشاء سلاسل رسائل وتشغيلها، نحتاج إلى إنشاء فئة، وجعلها ترث java.lang . فئة مؤشر الترابط ، وتجاوز طريقة التشغيل () الخاصة بها. وهذا الشرط الأخير مهم جدا. في طريقة التشغيل () نحدد المنطق الذي سيتم تنفيذ مؤشر الترابط الخاص بنا فيه. الآن، إذا قمنا بإنشاء وتشغيل مثيل لـ MyFirstThread ، فستعرض طريقة run() سطرًا باسم: تعرض طريقة getName() اسم "النظام" الخاص بمؤشر الترابط، والذي يتم تعيينه تلقائيًا. ولكن لماذا نتحدث مبدئيا؟ دعونا ننشئ واحدًا ونكتشف ذلك!
public class Main {

   public static void main(String[] args) {

       for (int i = 0; i < 10; i++) {

           MyFirstThread thread = new MyFirstThread();
           thread.start();
       }
   }
}
إخراج وحدة التحكم: أنا خيط! اسمي الموضوع-2 أنا الموضوع! اسمي الموضوع-1 أنا الموضوع! اسمي الموضوع-0 أنا الموضوع! اسمي الموضوع-3 أنا الموضوع! اسمي الموضوع-6 أنا الموضوع! اسمي الموضوع-7 أنا الموضوع! اسمي Thread-4 أنا خيط! اسمي الموضوع-5 أنا الموضوع! اسمي الموضوع-9 أنا الموضوع! اسمي Thread-8 لننشئ 10 سلاسل ( كائنات MyFirstThread ، التي ترث Thread ) ونبدأها عن طريق استدعاء طريقة start () على كل كائن. بعد استدعاء أسلوب start() ، يتم تنفيذ المنطق في أسلوب run() . ملحوظة: أسماء المواضيع غير مرتبة. من الغريب أنهم لم يكونوا بالتسلسل: Thread-0 و Thread-1 و Thread-2 وما إلى ذلك؟ وكما حدث، فهذا مثال على الوقت الذي لا يناسب فيه التفكير "التسلسلي". تكمن المشكلة في أننا قدمنا ​​فقط أوامر لإنشاء وتشغيل 10 سلاسل رسائل. يقرر برنامج جدولة الخيط، وهو آلية خاصة لنظام التشغيل، ترتيب التنفيذ. إن تصميمها الدقيق وإستراتيجية اتخاذ القرار هما موضوعان للمناقشة العميقة التي لن نتعمق فيها الآن. الشيء الرئيسي الذي يجب تذكره هو أن المبرمج لا يمكنه التحكم في ترتيب تنفيذ الخيوط. لفهم مدى خطورة الموقف، حاول تشغيل الطريقة main() في المثال أعلاه عدة مرات. إخراج وحدة التحكم في التشغيل الثاني: أنا خيط! اسمي الموضوع-0 أنا الموضوع! اسمي Thread-4 أنا خيط! اسمي الموضوع-3 أنا الموضوع! اسمي الموضوع-2 أنا الموضوع! اسمي الموضوع-1 أنا الموضوع! اسمي الموضوع-5 أنا الموضوع! اسمي الموضوع-6 أنا الموضوع! اسمي Thread-8 أنا خيط! اسمي الموضوع-9 أنا الموضوع! اسمي هو إخراج وحدة التحكم Thread-7 من التشغيل الثالث: أنا Thread! اسمي الموضوع-0 أنا الموضوع! اسمي الموضوع-3 أنا الموضوع! اسمي الموضوع-1 أنا الموضوع! اسمي الموضوع-2 أنا الموضوع! اسمي الموضوع-6 أنا الموضوع! اسمي Thread-4 أنا خيط! اسمي الموضوع-9 أنا الموضوع! اسمي الموضوع-5 أنا الموضوع! اسمي الموضوع-7 أنا الموضوع! اسمي الموضوع-8

المشاكل الناجمة عن تعدد العمليات

في مثالنا مع الكتب، رأيت أن تعدد مؤشرات الترابط يحل مهام مهمة جدًا ويمكن أن يجعل برامجنا أسرع. في كثير من الأحيان أسرع عدة مرات. لكن تعدد مؤشرات الترابط يعتبر موضوعًا صعبًا. وفي الواقع، إذا تم استخدامها بشكل غير صحيح، فإنها تخلق المشاكل بدلاً من حلها. عندما أقول "يخلق مشاكل"، لا أقصد بالمعنى المجرد. هناك مشكلتان محددتان يمكن أن يؤدي إليهما تعدد العمليات: حالة الجمود وظروف السباق. حالة الجمود هي حالة تنتظر فيها سلاسل رسائل متعددة الموارد التي تحتفظ بها بعضها البعض، ولا يمكن لأي منها الاستمرار في العمل. سنتحدث عنها أكثر في الدروس القادمة. يكفي المثال التالي في الوقت الحالي: تعدد مؤشرات الترابط في جافا: ما هو، فوائده والمزالق الشائعة - 4تخيل أن Thread-1 يتفاعل مع بعض الكائنات-1، وأن Thread-2 يتفاعل مع الكائن-2. علاوة على ذلك، تم كتابة البرنامج بحيث:
  1. يتوقف Thread-1 عن التفاعل مع Object-1 ويتحول إلى Object-2 بمجرد توقف Thread-2 عن التفاعل مع Object-2 ويتحول إلى Object-1.
  2. يتوقف Thread-2 عن التفاعل مع Object-2 ويتحول إلى Object-1 بمجرد توقف Thread-1 عن التفاعل مع Object-1 ويتحول إلى Object-2.
حتى بدون فهم عميق لتعدد العمليات، يمكنك أن ترى بسهولة أنه لن يحدث شيء. لن تقوم الخيوط بتبديل أماكنها أبدًا وستنتظر بعضها البعض إلى الأبد. يبدو الخطأ واضحا، لكنه في الواقع ليس كذلك. يمكنك القيام بذلك بسهولة في البرنامج. سننظر في أمثلة التعليمات البرمجية التي تسبب حالة توقف تام في الدروس اللاحقة. بالمناسبة، لدى Quora مثال واقعي رائع يشرح معنى الجمود . "في بعض الولايات في الهند، لن يبيعوا لك أرضًا زراعية إلا إذا كنت مزارعًا مسجلاً. ومع ذلك، لن يقوموا بتسجيلك كمزارع إذا لم تكن تمتلك أرضًا زراعية. عظيم! ماذا يمكن أن نقول؟! :) الآن دعونا نتحدث عن ظروف السباق. حالة السباق هي خطأ في التصميم في نظام أو تطبيق متعدد الخيوط، حيث يعتمد تشغيل النظام أو التطبيق على الترتيب الذي يتم به تنفيذ أجزاء من التعليمات البرمجية. تذكر مثالنا حيث بدأنا المواضيع:
public class MyFirstThread extends Thread {

   @Override
   public void run() {
       System.out.println("Thread executed: " + getName());
   }
}

public class Main {

   public static void main(String[] args) {

       for (int i = 0; i < 10; i++) {

           MyFirstThread thread = new MyFirstThread();
           thread.start();
       }
   }
}
تخيل الآن أن البرنامج هو المسؤول عن تشغيل الروبوت الذي يقوم بطهي الطعام! الخيط 0 يخرج البيض من الثلاجة. يتم تشغيل الخيط 1 على الموقد. يحصل الخيط 2 على مقلاة ويضعها على الموقد. الخيط 3 يضيء الموقد. الخيط 4 يصب الزيت في المقلاة. الخيط 5 يكسر البيض ويصبه في المقلاة. يقوم الخيط 6 بإلقاء قشر البيض في سلة المهملات. يقوم الخيط 7 بإزالة البيض المطبوخ من الموقد. الخيط 8 يضع البيض المطبوخ على طبق. الخيط 9 يغسل الأطباق. انظر إلى نتائج برنامجنا: تم تنفيذ الموضوع: Thread-0 تم تنفيذ الموضوع: Thread-2 تم ​​تنفيذ الموضوع Thread-1 تم تنفيذ الموضوع: Thread-4 تم تنفيذ الموضوع: Thread-9 تم تنفيذ الموضوع: Thread-5 تم تنفيذ الموضوع: Thread-8 Thread تم التنفيذ: الموضوع-7 تم تنفيذ الموضوع: الموضوع-3 هل هذا روتين كوميدي؟ :) وكل ذلك لأن عمل برنامجنا يعتمد على ترتيب تنفيذ المواضيع. مع أدنى انتهاك للتسلسل المطلوب، يتحول مطبخنا إلى جحيم، ويدمر الروبوت المجنون كل شيء من حوله. هذه أيضًا مشكلة شائعة في البرمجة متعددة الخيوط. سوف تسمع عنها أكثر من مرة. في ختام هذا الدرس، أود أن أوصي بكتاب حول تعدد العمليات. تعدد مؤشرات الترابط في Java: ما هو وفوائده ومزالقه الشائعة - 6تمت كتابة "Java Concurrency in Practice" في عام 2006، لكنها لم تفقد أهميتها. إنه مخصص لبرمجة Java متعددة الخيوط - بدءًا من الأساسيات ووصولاً إلى الأخطاء والأنماط الأكثر شيوعًا. إذا قررت يومًا ما أن تصبح خبيرًا في تعدد مؤشرات الترابط، فهذا الكتاب يجب عليك قراءته. نراكم في الدروس القادمة! :)
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION