معنى - synchronized method in java




ماذا يعني "متزامن" يعني؟ (10)

ما هي الكلمة الرئيسية المتزامنة؟

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

تعمل الكتل أو الطرائق المتزامنة على منع تداخل مؤشر الترابط والتأكد من توافق البيانات. في أي وقت ، يمكن لموضوع واحد فقط الوصول إلى كتلة أو طريقة متزامنة ( القسم الحرج ) عن طريق الحصول على قفل. ينتظر مؤشر ترابط آخر من أجل تحرير القفل للوصول إلى مقطع حرج .

متى تتم مزامنة الطرق؟

تتم مزامنة الطرق عند إضافة synchronized لتعريف أو تعريف الأسلوب. يمكنك أيضًا مزامنة كتلة معينة من الشفرة بطريقة ما.

ماذا يعني pro نحويًا ومنطقيًا؟

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

هذا لا يمكن القيام به مع السحر. انها مبرمج المسؤولية لتحديد القسم (المقاطع) الحرجة في التطبيق وحراستها وفقا لذلك. توفر Java إطارًا لحراسة تطبيقك ، ولكن أين وما هي الأقسام التي يجب أن تخضع للحراسة هي مسؤولية مبرمج.

مزيد من التفاصيل من page وثائق جافا

القفل الداخلي والتزامن:

يعتمد التزامن حول كيان داخلي يعرف باسم القفل الداخلي أو قفل الشاشة. تلعب الأقفال الجوهرية دورًا في كلا جانبي التزامن: فرض الوصول الحصري لحالة الكائن وإنشاء علاقات حدث قبل أن تكون ضرورية للرؤية.

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

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

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

إن جعل الطرق المتزامنة له effects :

أولاً ، لا يمكن استدعاء اثنين من الأساليب المتزامنة على نفس الكائن.

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

ثانياً ، عندما تخرج طريقة متزامنة ، تقوم تلقائياً بإنشاء علاقة حدث قبل مع أي استدعاء لاحق لطريقة متزامنة لنفس الكائن.

يضمن ذلك أن التغييرات في حالة الكائن مرئية لكل سلاسل الرسائل.

ابحث عن بدائل أخرى للتزامن في:

تجنب متزامنة (هذا) في جاوة؟

لدي بعض الأسئلة بخصوص استخدام وأهمية الكلمة الرئيسية synchronized .

  • ما هي أهمية الكلمة الأساسية synchronized ؟
  • متى يجب synchronized الطرق؟
  • ماذا يعني برمجيًا ومنطقيًا؟

نظرة عامة

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

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

أعمق

بناء على صيغة الجملة ، تأخذ الكلمة الرئيسية synchronized كائنًا كمعلمة (تسمى كائن قفل ) ، ثم يتبعها { block of code } .

  • عندما يواجه التنفيذ هذه الكلمة الرئيسية ، يحاول مؤشر الترابط الحالي "تأمين / الحصول / امتلاك" (اختيار) كائن القفل وتنفيذ كتلة التعليمات البرمجية المرتبطة بعد الحصول على القفل.

  • يتم ضمان أي الكتابة إلى المتغيرات داخل كتلة التعليمات البرمجية المتزامنة لتكون مرئية لكل مؤشر ترابط آخر يقوم بالمثل بتنفيذ التعليمات البرمجية داخل كتلة تعليمات برمجية متزامنة باستخدام نفس كائن القفل .

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

طرق متزامنة:

إضافة كلمة رئيسية synchronized إلى تعريف أسلوب يساوي جسم الطريقة بالكامل الذي تم لفه في كتلة تعليمة برمجية متزامنة مع كائن القفل this (على سبيل المثال أساليب) و ClassInQuestion.getClass() (لطرق الفئة) .

- طريقة المثيل هي طريقة لا تحتوي على static رئيسية static .
- طريقة التصنيف هي طريقة تحتوي على كلمة رئيسية static .

تقني

بدون التزامن ، لا يتم ضمان أي ترتيب تتم فيه عمليات القراءة والكتابة ، مع ترك المتغير مع القمامة.
(على سبيل المثال ، يمكن أن ينتهي المتغير بنصف البتات التي كتبها مؤشر واحد ونصف من البتات التي كتبها مؤشر ترابط آخر ، تاركًا المتغير في حالة لم يحاول أي من السائلين كتابتها ، ولكن فوضى مشتركة لكل منهما.)

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

استنتاج

وبالتالي في حالة Java ، يجب عليك اتباع Java Memory Model لضمان عدم حدوث أخطاء الربط.
بمعنى آخر: استخدم المزامنة أو العمليات الذرية أو الفئات التي تستخدمها لك تحت الغطاء.

مصادر

http://docs.oracle.com/javase/specs/jls/se8/html/index.html
مواصفات لغة Java® ، 2015-02-13


إلى فهمي متزامنة أساسا يعني أن المجمع يكتب جهاز monitor.enter و monitor.exit حول الأسلوب الخاص بك. على هذا النحو ، قد يكون خيطًا آمنًا اعتمادًا على كيفية استخدامه (ما أقصده هو أنه يمكنك كتابة كائن بطرق متزامنة غير خيالية اعتماداً على ما يفعله فصلك).


الكلمة الأساسية synchronized هي كل شيء عن مختلف المواضيع القراءة والكتابة إلى نفس المتغيرات والكائنات والموارد. هذا ليس موضوعًا تافهًا في Java ، ولكن هنا اقتباس من Sun:

synchronized الطرق synchronized تمكين إستراتيجية بسيطة لمنع تداخل الخيط وأخطاء تناسق الذاكرة: إذا كان الكائن مرئيًا لأكثر من سلسلة واحدة ، تتم جميع عمليات القراءة أو الكتابة إلى متغيرات ذلك الكائن من خلال طرق متزامنة.

في اختصار صغير جدًا جدًا: عندما يكون لديك خيوطان تقرأان وتكتبان إلى نفس "المصدر" ، قل المتغير المسمى foo ، فأنت بحاجة إلى التأكد من أن سلاسل الرسائل هذه تصل إلى المتغير بطريقة ذرية. بدون كلمة رئيسية synchronized ، قد لا يرى مؤشر الترابط 1 مؤشر ترابط التغيير 2 الذي تم إجراؤه على foo ، أو ما هو أسوأ من ذلك ، قد لا يتغير إلا النصف. هذا لن يكون ما تتوقعه منطقيا.

مرة أخرى ، هذا موضوع غير تافه في Java. لمعرفة المزيد ، استكشف الموضوعات هنا في SO و Interwebs حول:

استمر في استكشاف هذه المواضيع حتى يصبح اسم "Brian Goetz" مرتبطًا دائمًا بمصطلح "التزامن" في عقلك.


تمنع الكلمة الرئيسية المتزامنة الوصول المتزامن إلى كتلة من التعليمة البرمجية أو الكائن بواسطة سلاسل عمليات متعددة. بشكل افتراضي ، تتم synchronized Hashtable ، بحيث يمكن الوصول إلى الجدول في وقت واحد فقط.

عند استخدام التركيبات non-synchronized مثل HashMap ، يجب إنشاء ميزات أمان مؤشر ترابط في التعليمات البرمجية الخاصة بك لمنع أخطاء تناسق الذاكرة.


حسنًا ، أعتقد أن لدينا ما يكفي من التفسيرات النظرية ، لذا فكر في هذا الرمز

public class SOP {
    public static void print(String s) {
        System.out.println(s+"\n");
    }
}

public class TestThread extends Thread {
    String name;
    TheDemo theDemo;
    public TestThread(String name,TheDemo theDemo) {
        this.theDemo = theDemo;
        this.name = name;
        start();
    }
    @Override
    public void run() {
        theDemo.test(name);
    }
}

public class TheDemo {
    public synchronized void test(String name) {
        for(int i=0;i<10;i++) {
            SOP.print(name + " :: "+i);
            try{
                Thread.sleep(500);
            } catch (Exception e) {
                SOP.print(e.getMessage());
            }
        }
    }
    public static void main(String[] args) {
        TheDemo theDemo = new TheDemo();
        new TestThread("THREAD 1",theDemo);
        new TestThread("THREAD 2",theDemo);
        new TestThread("THREAD 3",theDemo);
    }
}

ملاحظة: يقوم الكتل synchronized باستدعاء مؤشر الترابط التالي لاختبار الطريقة () طالما لم يتم الانتهاء من تنفيذ سلسلة العمليات السابقة. يمكن للخيوط الوصول إلى هذه الطريقة واحدة في كل مرة. بدون synchronized جميع الخيوط يمكن الوصول إلى هذه الطريقة في وقت واحد.

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

يحدث شيء مشابه عندما يتم استدعاء أي طريقة متزامنة ثابتة للفئة. يكتسب مؤشر الترابط القفل المرتبط بالفئة (في هذه الحالة ، يمكن استدعاء أي طريقة غير ثابتة متزامنة لمثيل من هذه الفئة بأي مؤشر ترابط لأن قفل مستوى الكائن هذا لا يزال متاحًا). لن يتمكن أي مؤشر ترابط آخر من الاتصال بأي طريقة متزامنة ثابتة للفئة طالما لا يتم تحرير قفل مستوى الصف بواسطة مؤشر الترابط الذي يحمل القفل حاليًا.

الإخراج مع متزامنة

THREAD 1 :: 0
THREAD 1 :: 1
THREAD 1 :: 2
THREAD 1 :: 3
THREAD 1 :: 4
THREAD 1 :: 5
THREAD 1 :: 6
THREAD 1 :: 7
THREAD 1 :: 8
THREAD 1 :: 9
THREAD 3 :: 0
THREAD 3 :: 1
THREAD 3 :: 2
THREAD 3 :: 3
THREAD 3 :: 4
THREAD 3 :: 5
THREAD 3 :: 6
THREAD 3 :: 7
THREAD 3 :: 8
THREAD 3 :: 9
THREAD 2 :: 0
THREAD 2 :: 1
THREAD 2 :: 2
THREAD 2 :: 3
THREAD 2 :: 4
THREAD 2 :: 5
THREAD 2 :: 6
THREAD 2 :: 7
THREAD 2 :: 8
THREAD 2 :: 9

الإخراج دون متزامنة

THREAD 1 :: 0
THREAD 2 :: 0
THREAD 3 :: 0
THREAD 1 :: 1
THREAD 2 :: 1
THREAD 3 :: 1
THREAD 1 :: 2
THREAD 2 :: 2
THREAD 3 :: 2
THREAD 1 :: 3
THREAD 2 :: 3
THREAD 3 :: 3
THREAD 1 :: 4
THREAD 2 :: 4
THREAD 3 :: 4
THREAD 1 :: 5
THREAD 2 :: 5
THREAD 3 :: 5
THREAD 1 :: 6
THREAD 2 :: 6
THREAD 3 :: 6
THREAD 1 :: 7
THREAD 2 :: 7
THREAD 3 :: 7
THREAD 1 :: 8
THREAD 2 :: 8
THREAD 3 :: 8
THREAD 1 :: 9
THREAD 2 :: 9
THREAD 3 :: 9

ما تفتقده الإجابات الأخرى هو أحد الجوانب الهامة: حواجز الذاكرة . يتألف تزامن سلاسل الرسائل أساسًا من جزأين: التسلسل والرؤية. أنصح الجميع ببحث Google عن "حاجز ذاكرة jvm" ، نظرًا لأنه موضوع غير تافه ومهم للغاية (إذا قمت بتعديل البيانات المشتركة التي يتم الوصول إليها من خلال سلاسل رسائل متعددة). بعد القيام بذلك ، أنصحك بالاطلاع على فصول java.util.concurrent package التي تساعد على تجنب استخدام المزامنة الواضحة ، والتي بدورها تساعد في إبقاء البرامج بسيطة وفعالة ، وربما حتى منع الجمود.

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

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

تأثيرات تناسق الذاكرة: كما هو الحال مع المجموعات المتزامنة الأخرى ، الإجراءات في مؤشر ترابط قبل وضع كائن في ConcurrentLinkedDeque happen-before الإجراءات التالية للوصول أو إزالة هذا العنصر من ConcurrentLinkedDeque في مؤشر ترابط آخر.

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


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


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

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

Object.wait()

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

class MyRunnable implements Runnable {
    int var = 10;
    @Override
    public void run() {
        call();
    }

    public void call() {
        synchronized (this) {
            for (int i = 0; i < 4; i++) {
                var++;
                System.out.println("Current Thread " + Thread.currentThread().getName() + " var value "+var);
            }
        }
    }
}

public class MutlipleThreadsRunnable {
    public static void main(String[] args) {
        MyRunnable runnable1 = new MyRunnable();
        MyRunnable runnable2 = new MyRunnable();
        Thread t1 = new Thread(runnable1);
        t1.setName("Thread -1");
        Thread t2 = new Thread(runnable2);
        t2.setName("Thread -2");
        Thread t3 = new Thread(runnable1);
        t3.setName("Thread -3");
        t1.start();
        t2.start();
        t3.start();
    }
}

لقد أنشأنا اثنين من الكائنات فئة MyRunnable ، runnable1 يجري تقاسمها مع الموضوع 1 والخيط 3 و runnable2 تقاسمها مع موضوع 2 فقط. الآن ، عندما يبدأ t1 و t3 بدون استخدام متزامن ، خرج PFB الذي يشير إلى أن كلا الخيوطين 1 و 3 يؤثران في نفس الوقت على قيمة var حيث بالنسبة لـ thread 2 ، يكون var ذاكرته الخاصة.

Without Synchronized keyword

    Current Thread Thread -1 var value 11
    Current Thread Thread -2 var value 11
    Current Thread Thread -2 var value 12
    Current Thread Thread -2 var value 13
    Current Thread Thread -2 var value 14
    Current Thread Thread -1 var value 12
    Current Thread Thread -3 var value 13
    Current Thread Thread -3 var value 15
    Current Thread Thread -1 var value 14
    Current Thread Thread -1 var value 17
    Current Thread Thread -3 var value 16
    Current Thread Thread -3 var value 18

باستخدام Synchronzied ، مؤشر ترابط 3 انتظار مؤشر ترابط 1 لإكمال في كافة السيناريوهات. يوجد قفلان تم الحصول عليهما ، أحدهما على runnable1 مشترك بواسطة مؤشر الترابط 1 و مؤشر الترابط 3 والآخر على runnable2 مشترك بواسطة مؤشر الترابط 2 فقط.

Current Thread Thread -1 var value 11
Current Thread Thread -2 var value 11
Current Thread Thread -1 var value 12
Current Thread Thread -2 var value 12
Current Thread Thread -1 var value 13
Current Thread Thread -2 var value 13
Current Thread Thread -1 var value 14
Current Thread Thread -2 var value 14
Current Thread Thread -3 var value 15
Current Thread Thread -3 var value 16
Current Thread Thread -3 var value 17
Current Thread Thread -3 var value 18




synchronized