Syncكرونة في Java

ما هي تفاصيل Syncكرونة في Java?

In Javaيشير المزامنة إلى القدرة على التحكم في وصول خيوط متعددة إلى أي مورد مشترك. إنه خيار مثالي حيث نريد السماح لخيط واحد فقط بالوصول إلى المورد المشترك.

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

انواع من Syncكرونة

هناك نوعان من طرق المزامنة في Java:

1) مزامنة العملية

2) مزامنة الخيوط.

دعونا ندرس الموضوع و مزامنة العملية بالتفصيل.

مزامنة العملية: إنه يدير المزامنة بين البرامج. على سبيل المثال، برامج مثل `Microsoft Wordيتم تشغيل `` و`Acrobat Reader` كعمليات فردية.

مزامنة الخيوط: يُطلق على التنفيذ المتزامن للمورد المهم بواسطة خيطين أو أكثر اسم Thread Syncكرونة. يمكن تجميعك بشكل أكبر للتواصل المتبادل والترابط.

ما هو القفل في Java?

اربط Java مبني حول كيان داخلي يعرف بالشاشة أو القفل. كل الكائنات لها قفل مرتبط بها. لذلك، يجب أن يحصل الخيط الذي يحتاج إلى وصول متسق إلى حقول الكائن على قفل الكائن قبل الوصول إليه، ويحرر القفل عند انتهاء العمل. وهذا يضمن وصول مؤشر ترابط واحد فقط إلى البيانات المشتركة في المرة الواحدة.

برنامج متعدد الخيوط مع المزامنة

برنامج متعدد الخيوط هي طريقة أو كتلة محمية من التدخل من الخيوط الأخرى التي تشترك في نفس المورد المشار إليه باستخدام الكلمة الأساسية `synchronized`.

استخدام الطريقة المتزامنة

تُعرف أي طريقة يتم إعلانها على أنها متزامنة باسم طريقة متزامنة. كما تُستخدم لقفل كائن لأي مورد مشترك. لذا، عندما يستدعي مؤشر ترابط طريقة متزامنة، فإنه يستحوذ تلقائيًا على القفل الخاص بهذا الكائن ويطلقه عندما ينتهي من مهمته.

ملحوظة: لا يمكن استخدام الكلمة الأساسية synchronized مع الفئات والمتغيرات. يمكن استخدام الأساليب والكتل فقط مع الكلمة الأساسية.

لماذا استخدام Syncالطريقة المؤرشفة؟

  • يتم استخدامه لقفل كائن لأي موارد مشتركة.
  • يحصل الكائن على القفل كلما تم استدعاء الطريقة المتزامنة.
  • لا يتم تحرير القفل حتى يكمل الخيط وظيفته

بناء الجملة:

Acess_modifiers synchronized return_type method_name (Method_Parameters) {
}
class MathService {
    synchronized void getSumOfArray(int[] numbers) {
     int sum = 0;

         for (int number : numbers) {
             System.out.println(Thread.currentThread()
                     .getName()
                     + " adds "
                     + sum + " to "
                     + number + " to get -> "
                     + (sum += number));

             try {
                 Thread.sleep(500);
             } catch (InterruptedException e) {
                 throw new RuntimeException(e);
             }
         }
    }
}
public class Synchronization {
    public static void main(String[] args) {
        MathService mathService = new MathService();

        Thread threadOne = new Thread(() ->
                mathService.getSumOfArray(new int[]{10, 11, 12}));
        Thread threadTwo = new Thread(() ->
                mathService.getSumOfArray(new int[]{20, 21, 22}));

        threadOne.start();
        threadTwo.start();
    }
}

شرح الكود:

قم بتشغيل هذا المثال ولاحظ أن الخيط `0` يحصل على قفل كائن `mathService` أولاً ويستخدم هذا القفل حصريًا حتى يكتمل التنفيذ. لم يتم تشذير الخيط `0` و`1` في هذا الرمز. الإخراج كما هو موضح أدناه.

الإخراج:

Thread-0 adds 0 to 10 to get -> 10
Thread-0 adds 10 to 11 to get -> 21
Thread-0 adds 21 to 12 to get -> 33
Thread-1 adds 0 to 20 to get -> 20
Thread-1 adds 20 to 21 to get -> 41
Thread-1 adds 41 to 22 to get -> 63

استخدام كتلة متزامنة

لنفترض أنك لا تريد مزامنة الطريقة بأكملها. بدلاً من ذلك، تريد مزامنة بضعة أسطر من التعليمات البرمجية. في ذلك الوقت، Syncساعدت الكتلة المزخرفة في مزامنة العناصر المحددة Java رمز. Syncيمكن الوصول إلى أقفال الطريقة المنسقة على الطريقة، بينما يمكن الوصول إلى أقفال الكتلة المتزامنة على الكائن.

class MathService {
    void getSumOfArray(int[] numbers) {
        synchronized (this){
            int sum = 0;

            for (int number : numbers) {
                System.out.println(Thread.currentThread()
                        .getName()
                        + " adds "
                        + sum + " to "
                        + number + " to get -> "
                        + (sum += number));

                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}
public class Synchronization {
    public static void main(String[] args) {
        MathService mathService = new MathService();

        Thread threadOne = new Thread(() ->
                mathService.getSumOfArray(new int[]{10, 11, 12}));
        Thread threadTwo = new Thread(() ->
                mathService.getSumOfArray(new int[]{20, 21, 22}));

        threadOne.start();
        threadTwo.start();
    }
}

شرح الكود:

عند تشغيل هذا الكود، ستلاحظ أنه يعمل دون أي تدخل. في الطريقة المتزامنة، يتم تطبيق القفل بواسطة الطريقة، ولكن في الكتلة المتزامنة، يتم تطبيق القفل بواسطة الكائن. تأكد من أن الناتج كما هو موضح أدناه.

الإخراج:

Thread-0 adds 0 to 10 to get -> 10
Thread-0 adds 10 to 11 to get -> 21
Thread-0 adds 21 to 12 to get -> 33
Thread-1 adds 0 to 20 to get -> 20
Thread-1 adds 20 to 21 to get -> 41
Thread-1 adds 41 to 22 to get -> 63

شرح الكود:

عند تشغيل هذا الكود ستلاحظ أنه يعمل بدون أي تدخل وهو ما توقعناه ففي الطريقة المتزامنة يتم تطبيق القفل بواسطة الطريقة أما في طريقة الكتلة المتزامنة يتم تطبيق القفل بواسطة الكائن.

استخدام المزامنة الثابتة

In Java المزامنة، إذا كان هناك أكثر من كائن، فقد يكتسب خيطان الأقفال ويدخلان كتلة أو كتلة متزامنة، مع قفل منفصل لكل كائن. لتجنب ذلك، يمكن استخدام المزامنة الثابتة. Syncسيتم استخدام الكلمات الأساسية المنسقة قبل الأساليب الثابتة.

ملحوظة: في المزامنة الثابتة، يكون الوصول إلى القفل على الفصل، وليس على الكائن والطريقة.

رمز لتوضيح مشكلة قفل الكائنات المتعددة

class MathService {
    synchronized void getSumOfArray(int[] numbers) {
            int sum = 0;

            for (int number : numbers) {
                System.out.println(Thread.currentThread()
                        .getName()
                        + " adds "
                        + sum + " to "
                        + number + " to get -> "
                        + (sum += number));

                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
    }
}
public class Synchronization {
    public static void main(String[] args) {
        MathService mathService = new MathService();
        MathService mathService1 = new MathService();

        Thread threadOne = new Thread(() ->
                mathService.getSumOfArray(new int[]{10, 11, 12}));
        Thread threadTwo = new Thread(() ->
                mathService.getSumOfArray(new int[]{20, 21, 22}));
        Thread threadThree = new Thread(() ->
                mathService1.getSumOfArray(new int[]{10, 11, 12}));
        Thread threadFour = new Thread(() ->
                mathService1.getSumOfArray(new int[]{20, 21, 22}));

        threadOne.start();
        threadTwo.start();
        threadThree.start();
        threadFour.start();
    }
}

شرح الكود:

عندما نقوم بإنشاء مثيل آخر لـ "MathService"، فإننا نقدم تداخلًا في الخيوط حيث سيتم تشذيرها مع الكائنين. لاحظ أن الخيط `0` والخيط `2` مشذران مع الكائنين، في حين أن الخيط `1` و`3` مشذران مع الكائنين.

الإخراج:

Thread-0 adds 0 to 10 to get -> 10
Thread-2 adds 0 to 10 to get -> 10
Thread-0 adds 10 to 11 to get -> 21
Thread-2 adds 10 to 11 to get -> 21
Thread-0 adds 21 to 12 to get -> 33
Thread-2 adds 21 to 12 to get -> 33
Thread-1 adds 0 to 20 to get -> 20
Thread-3 adds 0 to 20 to get -> 20
Thread-1 adds 20 to 21 to get -> 41
Thread-3 adds 20 to 21 to get -> 41
Thread-1 adds 41 to 22 to get -> 63
Thread-3 adds 41 to 22 to get -> 63

نفس الكود باستخدام طريقة ثابتة متزامنة

class MathService {
    synchronized static void getSumOfArray(int[] numbers) {
            int sum = 0;

            for (int number : numbers) {
                System.out.println(Thread.currentThread()
                        .getName()
                        + " adds "
                        + sum + " to "
                        + number + " to get -> "
                        + (sum += number));

                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
    }
}
public class Synchronization {
    public static void main(String[] args) {
        MathService mathService = new MathService();
        MathService mathService1 = new MathService();

        Thread threadOne = new Thread(() ->
                mathService.getSumOfArray(new int[]{10, 11, 12}));
        Thread threadTwo = new Thread(() ->
                mathService.getSumOfArray(new int[]{20, 21, 22}));
        Thread threadThree = new Thread(() ->
                mathService1.getSumOfArray(new int[]{10, 11, 12}));
        Thread threadFour = new Thread(() ->
                mathService1.getSumOfArray(new int[]{20, 21, 22}));

        threadOne.start();
        threadTwo.start();
        threadThree.start();
        threadFour.start();
    }
}

قم بتشغيل الكود أعلاه ولاحظ أننا قمنا الآن بإزالة تداخل الخيط. يظهر إخراج الكود أدناه.

الإخراج:

Thread-0 adds 0 to 10 to get -> 10
Thread-0 adds 10 to 11 to get -> 21
Thread-0 adds 21 to 12 to get -> 33
Thread-3 adds 0 to 20 to get -> 20
Thread-3 adds 20 to 21 to get -> 41
Thread-3 adds 41 to 22 to get -> 63
Thread-2 adds 0 to 10 to get -> 10
Thread-2 adds 10 to 11 to get -> 21
Thread-2 adds 21 to 12 to get -> 33
Thread-1 adds 0 to 20 to get -> 20
Thread-1 adds 20 to 21 to get -> 41
Thread-1 adds 41 to 22 to get -> 63

مزايا استخدام المزامنة

فيما يلي مزايا العمل مع التطبيقات المتزامنة:

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

عيوب Syncآلية التسلسل

Syncآليات التسلسل لها أداء ضعيف.

مثلا

  • افترض أن هناك خمس عمليات، A1 وA2 وA3 وA4 وA5.
  • إنهم ينتظرون الموارد المشتركة للوصول إلى موضوع واحد في كل مرة.
  • تظل جميع العمليات في حالة انتظار، لذا يجب أن ينتظر الأخير في قائمة الانتظار حتى تكتمل جميع العمليات الأخرى.

الملخص

  • Syncيشير التسلسل إلى القدرة على التحكم في وصول سلاسل رسائل متعددة إلى أي مورد مشترك.
  • Java يحتوي على نوعين من طرق المزامنة: 1) مزامنة العملية و 2) مزامنة الخيوط.
  • اربط Java مبني حول كيان داخلي يعرف بالشاشة أو القفل.
  • البرنامج متعدد الخيوط هو طريقة أو كتلة محمية من التداخل من الخيوط الأخرى التي تشترك في نفس المورد المشار إليه باستخدام الكلمة الأساسية `synchronized`.
  • أي طريقة يتم إعلانها على أنها متزامنة تُعرف بالطريقة المتزامنة.
  • In Javaيمكن الوصول إلى أقفال الطريقة المتزامنة على الطريقة، بينما يمكن الوصول إلى أقفال الكتلة المتزامنة على الكائن.
  • في المزامنة الثابتة، يكون الوصول إلى القفل على الفصل، وليس على الكائن والطريقة.
  • الهدف الرئيسي للمزامنة في Java هو منع البيانات غير المتسقة عن طريق منع تداخل الخيوط.
  • أكبر عيب في هذه الطريقة هو أن جميع العمليات تظل في حالة انتظار، لذا يجب أن تنتظر العملية الأخيرة في قائمة الانتظار حتى تكتمل جميع العمليات الأخرى.