تصف هذه المقالة الفرق بين متزامن (قفل الكائن) والمتزامن الثابت (قفل الفئة) في Java. شاركه للرجوع إليه ، على النحو التالي:
الفرق بين متزامن وساكنة متزامنة
من خلال تحليل تحليل هذين الاستخدامين ، يمكننا أن نفهم مفهوم القفل في Java. أحدهما قفل مثيل (مغلق على كائن مثيل. إذا كان الفصل هو المفرد ، فإن القفل لديه أيضًا مفهوم قفل عالمي) ، والآخر هو قفل عالمي (يتم استهداف القفل في فئة ما. بغض النظر عن عدد الكائنات التي يكون عليها المثيل ، تشترك مؤشرات الترابط في القفل). يتوافق قفل المثيل مع الكلمة الرئيسية المتزامنة ، بينما يتوافق قفل الفئة (القفل العالمي) مع الكائن الثابت المتزامن (أو مغلق على فئة أو كائن فئة من الفئة).
المقالة التالية تعطي ملخصًا جيدًا:
1. الفرق بين متزامن و static متزامن
يقفل متزامن المثيل الحالي (الكائن الحالي) للفئة لمنع مؤشرات الترابط الأخرى من الوصول إلى جميع الكتل المتزامنة في مثيل الفئة في نفس الوقت. لاحظ أن هذا هو "الحالة الحالية للفئة". لا يوجد مثل هذا القيد على حالتين مختلفتين من الفصل.
ثم يحدث المزامنة الثابتة للتحكم في الوصول المتزامن لجميع مثيلات الفصل ، وتقييد المزامنة الثابتة جميع مثيلات الفصل في متعدد الخيوط للوصول إلى كتلة الكود المقابلة للفئة في JVM في نفس الوقت. في الواقع ، إذا تمت مزامنتها في طريقة أو كتلة رمز في فئة ، بعد إنشاء مثيل من الفئة ، سيكون للمثال أيضًا كتلة مراقبة لمنع مؤشرات الترابط من الوصول إلى كتلة الحماية المتزامنة في وقت متزامن. المزامنة الثابتة هي كتلة مراقبة شائعة في جميع حالات الفصل. هذا هو الفرق بين الاثنين. وبعبارة أخرى ، فإن المزامنة تعادل هذا. (موجهة لاحقًا)
يحتوي مؤلف ياباني ، "نمط التصميم متعدد مؤشرات الترابط على Jai Chenghao" على عمود مثل هذا:
الفئة pulbic some () {{public synchronized void issynca () {} issyncb () issyncb () {}لذا ، إذا كان هناك حالتان x و y لفئة شيء ما ، فما هي الحالة التي يتم فيها الوصول إلى المجموعة التالية من الطرق في وقت واحد بواسطة مؤشرات ترابط متعددة؟
Axissynca () و x.issyncb ()
bxissynca () و y.issynca ()
cxcsynca () و y.csyncb ()
dxissynca () وشيء. csynca ()
هنا ، من الواضح أنه يمكن الحكم عليه:
A ، جميعها تصل المجال المتزامن إلى نفس المثال (X) ، وبالتالي لا يمكن الوصول إليها في وقت واحد. (لا يمكن الوصول إلى المجالات المتزامنة المختلفة التي يمكن الوصول إليها x في multithreads في وقت واحد)
إذا تم الوصول إلى x.issynca () في مؤشرات ترابط متعددة ، لأنه لا يزال نفس الحالة ويُغلق على نفس الطريقة ، لا يمكن الوصول إليه في سلسلة رسائل متعددة في نفس الوقت. (لا يمكن الوصول إلى نفس المجال المتزامن الذي يصل إلى x في multithreads في نفس الوقت)
ب ، مخصص لمثيلات مختلفة ، بحيث يمكن الوصول إليه في نفس الوقت (أقفال الكائنات لا تحتوي على قيود على قفل لمثيلات كائن مختلفة)
C ، نظرًا لأنه متزامن ثابت ، ستظل مثيلات مختلفة مقيدة ، وهو ما يعادل شيئًا. sissynca () وشيء. sissyncb () ، لذلك لا يمكن الوصول إليه في نفس الوقت.
لذا ، ماذا عن د؟ ، يمكن الوصول إلى الإجابة في الكتاب في وقت واحد. سبب الإجابة هو أن متزامنه هو أن طريقة المثيل وطريقة الفئة المتزامنة تختلفان عن القفل.
يعني التحليل الشخصي أن المتزامنة المتزامنة والساكنة تعادل عصابتين ، كل منها له سيطرته الخاصة ، ولا يوجد أي قيد على بعضهما البعض ويمكن الوصول إليه في نفس الوقت.
على سبيل المثال:
الفئة العامة testsynchronized {public synchronized void test1 () {int i = 5 ؛ بينما (i--> 0) {system.out.println (thread.currentThread (). getName () + ":" + i) ؛ حاول {thread.sleep (500) ؛ } catch (interruptedException ie) {}}} test static static static static test2 () {int i = 5 ؛ بينما (i--> 0) {system.out.println (thread.currentThread (). getName () + ":" + i) ؛ حاول {thread.sleep (500) ؛ } catch (interruptedException ie) {}}} public static void main (string [] args) {final testsynchronized myt2 = new testsynchronized () ؛ Thread Test1 = New Thread (new RunNable () {public void run () {myt2.test1 () ؛}} ، "test1") ؛ Thread Test2 = New Thread (New RunNable () {public void run () {testSyNchronized.test2 () ؛}} ، "test2") ؛ test1.start () ؛ test2.start () ؛ // testrunnable tr = new testrunnable () ؛ // thind test3 = new thread (tr) ؛ // test3.start () ؛ }}Test1: 4 Test2: 4 Test1: 3 Test2: 3 Test2: 2 Test1: 2 Test2: 1 Test1: 1 Test1: 0 Test2: 0
يعدل الشفرة المزامنة أعلاه الطريقة الثابتة وطريقة المثيل في نفس الوقت ، ولكن يتم تنفيذ نتائج التشغيل بالتناوب ، مما يثبت أن أقفال الفئة وأقفال الكائنات هي قفلان مختلفان ، يتحكمان في مناطق مختلفة ، ولا تتداخل مع بعضها البعض. وبالمثل ، في حين أن المواضيع تحصل على أقفال كائن ، يمكنها أيضًا الحصول على هذا النوع من القفل ، أي الحصول على قفلان في نفس الوقت ، وهو أمر مسموح به.
ختاماً:
ج: ثابت متزامن هو نطاق فئة معينة. يمنع CSYNC {} الثابتة المتزامنة مثيلات متعددة في مؤشرات ترابط متعددة من الوصول إلى الطريقة الثابتة المتزامنة في هذه الفئة في نفس الوقت. إنه يعمل على جميع مثيلات الكائنات من الفصل.
ب: متزامن هو نطاق مثيل. يمنع requync () {} المتماسك هذه المثيل من الوصول إلى الطريقة المتزامنة لهذه الفئة في نفس الوقت.
في الواقع ، من السهل جدًا تلخيصها.
2. الفرق بين الطريقة المتزامنة والرمز المتزامن بسرعة
لا يوجد فرق بين الأساليب المتزامنة () {} والمزامنة (هذا) {} ، ولكن الطرق المتزامنة () {} مريحة لفهم القراءة ، بينما يمكن أن تتم مزامنة (هذا) {} بشكل أكثر دقة تحكم مناطق الوصول إلى الصراع ، وأحيانًا تؤدي بشكل أكثر كفاءة.
مقارنة الكفاءة بين طريقتين:
1. مزامنة الكتلة ، الرمز كما يلي:
استيراد java.util.concurrent.countdownlatch ؛ استيراد java.util.concurrent.executorservice ؛ استيراد java.util.concurrent.executors ؛ الفئة العامة testsynchronized { / ** * param args * / public static void main (string [] args) {executorService service = evelysors.newcachedthreadpool () ؛ cdorder codorder النهائي = countDownLatch الجديد (1) ؛ Countdownlatch Cdanswer = New CountDownLatch (3) ؛ SynchonizedClass النهائي SC = جديد synchonizedclass () ؛ لـ (int i = 0 ؛ i <3 ؛ i ++) {runNable Runnable = new RunNable () {public void run () {try {cdorder.await () ؛ Sc.Start () ؛ cdanswer.countdown () ؛ } catch (استثناء e) {E.PrintStackTrace () ؛ }}} ؛ service.execute (runnable) ؛ } جرب {thread.sleep ((long) (Math.Random ()*10000)) ؛ System.out.println ("thread" + thread.currentThRead (). getName () + "publish execution command") ؛ cdorder.countdown () ؛ long begintime = system.currentTimeMillis () ؛ System.out.println ("thread" + thread.currentThRead (). getName () + "تم إرسال الأمر ، في انتظار النتيجة") ؛ cdanswer.await () ؛ System.out.println ("thread" + thread.currentThread (). getName ()) + "تم استلام جميع نتائج الاستجابة ، والوقت المستغرق هو:" + (System.CurrentTimeMillis ()-begintime)) ؛ } catch (استثناء e) {E.PrintStackTrace () ؛ } service.shutdown () ؛ }} class synchonizedClass {public void start () remrowsededException {thread.sleep (100) ؛ // تنفيذ منطق آخر لاستهلاك الوقت المتزامن (هذا) {system.out.println ("لقد ركضت مع 10 مللي ثانية") ؛ }}} نتائج التشغيل كما يلي:
يطلق الموضوع الرئيسي أمر التنفيذ ، وقد أرسل مؤشر الترابط الرئيسي الأمر ، في انتظار النتيجة التي قمت بتشغيلها واستخدمت 10 مللي ثانية
لقد قمت باستخدام 10 مللي ثانية
لقد قمت باستخدام 10 مللي ثانية
تلقى الخيط الرئيسي جميع نتائج الاستجابة ، والوقت المستغرق هو: 110
طريقة التزامن ، الرمز كما يلي:
استيراد java.util.concurrent.countdownlatch ؛ استيراد java.util.concurrent.executorservice ؛ استيراد java.util.concurrent.executors ؛ الفئة العامة testsynchronized { / ** * param args * / public static void main (string [] args) {executorService service = evelysors.newcachedthreadpool () ؛ cdorder codorder النهائي = countDownLatch الجديد (1) ؛ Countdownlatch Cdanswer = New CountDownLatch (3) ؛ SynchonizedClass النهائي SC = جديد synchonizedclass () ؛ لـ (int i = 0 ؛ i <3 ؛ i ++) {runNable Runnable = new RunNable () {public void run () {try {cdorder.await () ؛ Sc.Start () ؛ cdanswer.countdown () ؛ } catch (استثناء e) {E.PrintStackTrace () ؛ }}} ؛ service.execute (runnable) ؛ } جرب {thread.sleep ((long) (Math.Random ()*10000)) ؛ System.out.println ("thread" + thread.currentThRead (). getName () + "publish execution command") ؛ cdorder.countdown () ؛ long begintime = system.currentTimeMillis () ؛ System.out.println ("thread" + thread.currentThRead (). getName () + "تم إرسال الأمر ، في انتظار النتيجة") ؛ cdanswer.await () ؛ System.out.println ("thread" + thread.currentThread (). getName ()) + "تم استلام جميع نتائج الاستجابة ، والوقت المستغرق هو:" + (System.CurrentTimeMillis ()-begintime)) ؛ } catch (استثناء e) {E.PrintStackTrace () ؛ } service.shutdown () ؛ }} class SynchonizedClass {public synchronized void start () remrows interruptedException {thread.sleep (100) ؛ // تنفيذ وقت منطقي آخر // synchronized (this) {system.out.println ("لقد استخدمت 10 مللي ثانية") ؛ //}}}نتائج التشغيل كما يلي:
يطلق الموضوع الرئيسي أمر التنفيذ ، وقد أرسل مؤشر الترابط الرئيسي الأمر ، في انتظار النتيجة التي قمت بتشغيلها واستخدمت 10 مللي ثانية
لقد قمت باستخدام 10 مللي ثانية
لقد قمت باستخدام 10 مللي ثانية
تلقى الخيط الرئيسي جميع نتائج الاستجابة ، والوقت المستغرق هو: 332
الفرق بين الاثنين هو: 222ms.
توضح المقارنة أن كتل التعليمات البرمجية المتزامنة أكثر كفاءة من طرق التزامن.
الذاكرة التكميلية:
1. هناك نطاقان من الكلمات الرئيسية المتزامنة:
1) هو داخل مثيل كائن. يمكن أن تمنع Amethod () {} متزامنة متزامنة من الوصول إلى الطريقة المتزامنة لهذا الكائن في نفس الوقت (إذا كان للكائن طرقًا متعددة متزامنة ، طالما أن أحد مؤشرات ترابط يصل إلى أحد الطرق المتزامنة ، لا يمكن لخيوط أخرى الوصول إلى أي طرق متزامنة في الكائن في نفس الوقت). في هذا الوقت ، لا تنقطع الطريقة المتزامنة لحالات كائن مختلفة. وهذا يعني ، لا يزال بإمكان مؤشرات الترابط الأخرى الوصول إلى الطريقة المتزامنة في مثيل كائن آخر من نفس الفئة في نفس الوقت ؛
2) إنه نطاق فئة معينة. يمنع AstaticMethod الثابت المتزامن {} كائنات مثيل مختلفة (أو نفس كائن المثيل) في مؤشرات ترابط متعددة من الوصول إلى الطريقة الثابتة المتزامنة في هذه الفئة في نفس الوقت. إنه يعمل على جميع مثيلات الكائنات من الفصل.
2. بالإضافة إلى استخدام الكلمة الرئيسية المتزامنة قبل الطريقة ، يمكن أيضًا استخدام الكلمة الرئيسية المتزامنة في كتلة في الطريقة ، مما يشير إلى أنه يتم إجراء الوصول الحصري للطرفين فقط على موارد هذه الكتلة. الاستخدام هو: متزامن (هذا) {/*block*/} (أو متزامن (OBJ) {/*block*/}) ، ونطاقه هو الكائن الحالي ؛
3. لا يمكن مورث الكلمة الرئيسية المتزامنة. وهذا يعني أن طريقة الفئة الأساسية متزامنة f () {} لا يتم مزامنتها تلقائيًا f () {} في الفئة الموروثة ، ولكنها تصبح f () {}. تتطلب منك فئة الميراث تحديد صراحة أن واحدة من أساليبها متزامنة ؛
بعض فهم المزامنة (هذا) (اشرح قفل الكائن جيدًا ، انتبه إلى هذه الكلمة الرئيسية فيه)
1. عندما يصل اثنين من مؤشرات الترابط المتزامنة هذه كتلة الكود المتزامن (هذا) في نفس كائن الكائن ، يمكن تنفيذ مؤشر ترابط واحد فقط خلال فترة واحدة. يجب أن ينتظر مؤشر ترابط آخر حتى يقوم مؤشر الترابط الحالي بتنفيذ كتلة الرمز هذه قبل تنفيذ كتلة الكود.
2. ومع ذلك ، عندما يصل مؤشر ترابط واحد إلى كتلة رمز المزامنة (هذا) متزامن للكائن ، لا يزال بإمكان مؤشر ترابط آخر الوصول إلى كتلة رمز التزامن غير المتزامنة (هذا) في هذا الكائن.
3. من الأهمية بمكان أنه عندما يصل مؤشر ترابط إلى كتلة رمز المزامنة (هذا) متزامن للكائن ، فإن مؤشرات الترابط الأخرى ستحظر الوصول إلى جميع كتل رمز التزامن (هذا) متزامنة أخرى في الكائن.
4. ينطبق المثال الثالث أيضًا على كتل التعليمات البرمجية المتزامنة الأخرى. أي عندما يصل مؤشر ترابط إلى كتلة رمز المزامنة (هذا) متزامن لكائن ، فإنه يحصل على قفل كائن هذا الكائن. نتيجة لذلك ، يتم حظر مؤشرات الترابط الأخرى إلى جميع أجزاء الكود المتزامن لكائن الكائن مؤقتًا.
5. تنطبق القواعد أعلاه أيضًا على أقفال الكائنات الأخرى.
إضافة قطعة من التعليمات البرمجية لتسهيل اختبار الكلمات الرئيسية المتزامنة (تعديل بسيط)
الفئة العامة testsynchronized {public void test1 () {synchronized (this) {int i = 5 ؛ بينما (i--> 0) {system.out.println (thread.currentThread (). getName () + ":" + i) ؛ حاول {thread.sleep (500) ؛ } catch (interruptedException ie) {}}}} test 2 () {int i = 5 ؛ بينما (i--> 0) {system.out.println (thread.currentThread (). getName () + ":" + i) ؛ حاول {thread.sleep (500) ؛ } catch (interruptedException ie) {}}} test 3 () {int i = 5 ؛ بينما (i--> 0) {system.out.println (thread.currentThread (). getName () + ":" + i) ؛ حاول {thread.sleep (500) ؛ } catch (interruptedException ie) {}}} public static void main (string [] args) {final testsynchronized myt2 = new testsynchronized () ؛ testSynchronized myt3 = new testsynchronized () ؛ Thread Test1 = New Thread (New RunNable () {public void run () {myt2.test2 () ؛}} ، "test1") ؛ Thread Test2 = New Thread (New RunNable () {public void run () {myt2.test3 () ؛}} ، "test3") ؛ test1.start () ؛؛ test2.start () ؛ }} نتائج التشغيل:
Test1: 4test1: 3test1: 2test1: 1test1: 0test3: 4test3: 3test3: 2test3: 1test3: 0
نركز أدناه على استخدام Sychronized في Java ، وهو على وجه التحديد: طريقة المزامنة والكلمة الرئيسية المتزامنة الكتلة المتزامنة ، والتي تتضمن استخدامين: الطريقة المتزامنة والكتلة المتزامنة.
1. الطريقة المتزامنة: أعلن الطريقة المتزامنة عن طريق إضافة الكلمة الرئيسية المتزامنة إلى إعلان الطريقة. يحب:
Accessval المتزامن العام (int newval) ؛
تتحكم الطريقة المتزامنة في الوصول إلى متغيرات عضو الفئة: كل مثيل فئة يتوافق مع قفل ، ويجب أن تحصل كل طريقة متزامنة على قفل مثيل الفئة الذي يستدعي الطريقة قبل تنفيذها. خلاف ذلك ، يتم حظر الخيط الذي ينتمي إليه. بمجرد تنفيذ الطريقة ، سوف تشغل القفل حصريًا. لن يتم إصدار القفل حتى يعود من الطريقة. يمكن للخيط المحظور الحصول على القفل وإعادة إدخال الحالة القابلة للتنفيذ. تضمن هذه الآلية أنه في الوقت نفسه ، لكل مثيل فئة ، على الأكثر واحدة من جميع وظائف الأعضاء المعلنة أن تزامنه في حالة قابلة للتنفيذ (لأنه على الأكثر يمكن الحصول على القفل المقابل لمثيل الفصل) ، وبالتالي تجنب تعارضات الوصول بفعالية لمتغيرات أعضاء الفصل (طالما أن جميع الأساليب الممكنة للوصول إلى متغيرات أعضاء الفئة ، يتم إعلان التزامن).
في Java ، ليس فقط مثيلات الفصل ، كل فصل يتوافق أيضًا مع القفل ، حتى نتمكن من إعلان وظيفة العضو الثابت في الفصل على أنها ثابتة متزامنة للتحكم في وصولها إلى متغيرات الأعضاء الثابتة في الفصل.
عيب الطريقة المتزامنة: الإعلان عن طريقة كبيرة كما متزامن سيؤثر بشكل كبير على الكفاءة. عادةً ، إذا تم الإعلان عن طريقة فئة مؤشر الترابط () على أنها متزامنة ، حيث تم تشغيلها طوال عمر الخيط ، فلن يتسبب ذلك أبدًا في أي طريقة متزامنة في هذه الفئة. بالطبع يمكننا حل هذه المشكلة عن طريق وضع الكود الذي يصل إلى متغيرات عضو الفصل إلى طريقة خاصة ، وإعلانها على أنها متزامنة ، ووصفها بالطريقة الرئيسية ، لكن Java توفر لنا حلًا أفضل ، أي الكتلة المتزامنة.
2. كتلة متزامنة: أعلن الكتلة المتزامنة من خلال الكلمة الرئيسية المتزامنة. بناء الجملة كما يلي:
متزامن (syncobject) {// الكود الذي يسمح بالتحكم في الوصول} الكتلة المتزامنة هي كتلة رمز يجب أن يحصل فيها الرمز على قفل لكائن Syncobject (كما ذكرنا سابقًا ، يمكن أن يكون مثيلًا أو فئة فئة) قبل تنفيذها. الآلية المحددة هي نفسها كما هو موضح أعلاه. نظرًا لأنه يمكن استهدافه في أي كتلة رمز ويمكن تحديد كائنات مغلقة في أي وقت ، فهي أكثر مرونة.
يلاحظ:
عند استخدام الكلمات الرئيسية المتزامنة ، يجب عليك تجنب استخدام طرق النوم أو العائد في الطرق المتزامنة أو الكتل المتزامنة قدر الإمكان ، لأن كتل البرنامج المتزامنة تشغل أقفال كائنات ، لذلك إذا كنت ترتاح ، لا يمكن تنفيذ المواضيع الأخرى إلا أثناء انتظار الاستيقاظ والانتهاء من التنفيذ. ليس فقط أنه يؤثر بشكل خطير على الكفاءة ، بل إنه ليس منطقيًا أيضًا.
وبالمثل ، ليس من المنطقي استدعاء طريقة yeild في كتلة البرنامج المتزامن للتخلي عن مورد وحدة المعالجة المركزية ، لأنك تشغل القفل وغيرها من مؤشرات ترابط Mutex لا يمكن الوصول إلى كتلة البرنامج المتزامن. بالطبع ، يمكن أن تحصل المواضيع غير المرتبطة بكتل البرنامج المتزامنة على مزيد من وقت التنفيذ.
ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.