قبل Java 5 ، تم استخدام الكلمة الرئيسية المتزامنة لتنفيذ وظيفة القفل.
يمكن استخدام الكلمة الرئيسية المتزامنة كمعدل (طريقة متزامنة) أو كبيان داخل دالة (كتلة رمز متزامن).
لإتقان المزامنة ، المفتاح هو إتقان استخدام هذا الشيء كقفل. بالنسبة للطرق غير الستاطية (أساليب الأعضاء) للفئة ، فهذا يعني الحصول على قفل مثيل الكائن ؛ للطرق الثابتة (طرق الفئة) للفئة ، من الضروري الحصول على قفل كائن الفئة ؛ بالنسبة لكتل التعليمات البرمجية المتزامنة ، من الضروري تحديد قفل الكائن الذي يتم الحصول عليه. يمكن اعتبار الطرق غير المنتظمة المتزامنة بمثابة كتلة رمز متزامنة (هذا) {...} تحتوي على الطريقة بأكملها.
سواء كانت كتلة رمز متزامنة أو طريقة مزامنة ، يمكن أن يدخل مؤشر ترابط واحد فقط في وقت واحد (في معظم مؤشر ترابط واحد ينفذ قطاع التعليمات البرمجية في نفس الوقت.) ، وإذا كانت مؤشرات الترابط الأخرى تحاول الدخول (سواء كانت هي نفس الكتلة المتزامنة أو كتلة متزامنة مختلفة) ، فسيقوم JVM بتعليقها (وضعها في تجمع قفل الانتظار). يسمى هذا الهيكل قسمًا مهمًا في نظرية التزامن.
في JVM ، من أجل تحسين الكفاءة ، سيكون لكل مؤشر ترابط يعمل في نفس الوقت نسخة ذاكرة التخزين المؤقت من البيانات التي يتم معالجتها. عندما نستخدم المزامنة للمزامنة ، فإن ما يتم مزامنته حقًا هو كتلة الذاكرة التي تمثل الكائن المقفلة في مؤشرات ترابط مختلفة (ستبقى بيانات النسخ متزامنة مع الذاكرة الرئيسية. الآن نعرف لماذا يتم استخدام مزامنة كلمة). ببساطة ، بعد تنفيذ كتلة التزامن أو طريقة التزامن ، يجب كتابة أي تعديلات على الكائن المقفل مرة أخرى إلى الذاكرة الرئيسية قبل إطلاق القفل ؛ بعد إدخال كتلة التزامن والحصول على القفل ، تتم قراءة بيانات الكائن المقفل من الذاكرة الرئيسية ، ويجب مزامنة نسخة البيانات من مؤشر ترابط القفل مع عرض البيانات في الذاكرة الرئيسية.
فيما يلي أمثلة محددة لتوضيح المواقف المختلفة للمزامنة.
طريقة المزامنة المتزامنة
أولاً ، دعونا نلقي نظرة على مثال طريقة التزامن:
يمتد SynchronizedTest1 من الفئة العامة مؤشر الترابط {private synchronized void testsynchronizedMethod () {for (int i = 0 ؛ i <10 ؛ i ++) {system.out.println (thread.currentThRead (). getName () + "testsynchronizedmethod:" + i) ؛ حاول {thread.sleep (100) ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ }}} Override public void run () {testsynchronizedMethod () ؛ } public static void main (string [] args) {synchronizedt1 t = new SynchronizedTest1 () ؛ T.Start () ؛ T.TestsynchronizedMethod () ؛ }}تشغيل مخرجات البرنامج:
TestSynchronizedMethod الرئيسي: 0 Main TestsynchronizedMethod: 1 TrestsynchronizedMethod: 2 Main TestsynchronizedMethod: 3 Main TestSynchronizedMethod: 4 Main Testsynchronizedmethod: 5 Main Testsynchronizedmethod: 6 Main Testsynchonizedmethod: 7 Main Testsynchronizedmethed Main TestSynchronizedMethod: 9 Thread-0 TestSynchronizedMethod: 0 Thread-0 TestSynchronizedMethod: 1 Thread-0 TestSynchronizedMethod: 2 ThestsyNchronizedMethod: 3 thread-0 thread-0 trassynchronizedmethod: 4 thread-0 testsynchronizod TestSynchronizedMethod: 7 Thread-0 TestSynchronizedMethod: 8 Thread-0 TestSynchronizedMethod: 9
يمكنك أن ترى أن طريقة TestSynchronizedMethod يتم تنفيذها بشكل متزامن بين خيطين.
إذا تم تعديل الطريقة الرئيسية إلى ما يلي ، فلا يمكن تنفيذ مؤشر الترابط بشكل متزامن ، لأن شاشة التزامن للموضوعين ليسوا نفس الكائن ولا يمكن أن يلعب دورًا متزامنًا.
الفراغ الثابت العام (سلسلة [] args) {thread t = new SynchronizedTest1 () ؛ T.Start () ؛ الموضوع T1 = new SynchronizedTest1 () ؛ t1.start () ؛ }نتيجة الإخراج على النحو التالي:
Thread-0 TestSynchronizedMethod: 0 Thread-1 testsynchronizedMethod: 0 Thread-0 TestSynchronizedMethod: 1 Thread-1 testsynchronizedMethod: 1 Thread-0 TratsyNchronizedMethod: 3 thread-1 transsynchronizedmethod: 2 ThestsyNchronizedMethod: 3 thread-1 TestSynchronizedMethod: 4 thread-1 testsynchronizedmethod: 4 thread-0 testsynchronizedmethod: 5 thread-1 testsynchronizedmethod: 5 thread-0 trestsynchronizedmethod: 6 thread-1 trassynchronizedmethod: 6 thread-0 testsynchronized. TestSynchronizedMethod: 8 Thread-1 TestSynchronizedMethod: 8 Thread-0 TestSynchronizedMethod: 9 Thread-1 TestSynchronizedMethod: 9
إذا كان يمكن تشغيل الطريقة الرئيسية المعدلة بشكل متزامن بين خيطين ، فيجب إعلان طريقة TestSynchronizedMethod كطريقة ثابتة ، بحيث تكون شاشات الخيوط هي نفس الكائن (كائن الفئة) ويمكن تنفيذها بشكل متزامن. الرمز المعدل يبدو هكذا:
يمتد SynchronizedTest1 من الفئة العامة Thread {private static static void testsynchronizedMethod () {for (int i = 0 ؛ i <10 ؛ i ++) {system.out.println (thread.currentthroad (). getName () + "testsynchronizedmethod:" + i) ؛ حاول {thread.sleep (100) ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ }}} Override public void run () {testsynchronizedMethod () ؛ } public static void main (string [] args) {thread t = new SynchronizedTest1 () ؛ T.Start () ؛ الموضوع T1 = new SynchronizedTest1 () ؛ t1.start () ؛ }}نتيجة الإخراج على النحو التالي:
Thread-0 TestSynchronizedMethod: 0 Thread-0 TestSynchronizedMethod: 1 Thread-0 TestSynchronizedMethod: 2 Thread-0 TestSynchronizedMethod: 3 Thread-0 TestSynchronizedMethod: 4 ThinkNChronizedMethod TestSynchronizedMethod: 8 Thread-0 TestSynchronizedMethod: 9 Thread-1 testsynchronizedMethod: 0 thread-1 testsynchronizedmethod: 1 thread-1 testsynchronizedmethod: 2 thread-1 testsynchronizedMethod: 3 thread-1 testsynchronizedMethod: 4 thread-1 TestSynchronizedMethod: 7 Thread-1 TestSynchronizedMethod: 8 Thread-1 TestSynchronizedMethod: 9
يشبه موقف الكتل المتزامنة طريقة التزامن ، باستثناء أن الكتلة المتزامنة تقلل من تفريغ التحكم في التزامن ، والتي يمكن أن تمارس بشكل أفضل كفاءة التنفيذ المتوازي متعدد الخيوط.
استخدم هذا الكائن للتحكم في التزامن بين نفس مثيلات الكائن:
الفئة العامة SynchronizedTest2 يمتد مؤشر الترابط {private void testsynchronizedBlock () {synchronized (this) {for (int i = 0 ؛ i <10 ؛ i ++) {system.out.println (thread.currentThread (). getName () + " حاول {thread.sleep (100) ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ }}}} Override public void run () {testSyNchronizedBlock () ؛ } main static void main (string [] args) {synchronizedTest2 t = new SynchronizedTest2 () ؛ T.Start () ؛ T.TestsynchronizedBlock () ؛ }}نتيجة الإخراج:
TestSynchronizedBlock الرئيسي: 0 Main TestsynchronizedBlock: 1 اختبار رئيسي: 2 Main TestsynchronizedBlock: 3 Main TestsynchronizedBlock: 4 Main TrestSynchronizedBlock: 8 Main TestsynchronizedBlock: 6 Main TrestsynchronizedBlock: 7 Main TestsynchronizedBlock TestSynChronizedBlock: 0 Thread-0 TestSynchronizedBlock: 1 Thread-0 TestSynchronizedBlock: 2 Thread-0 TestsynchronizedBlock: 3 Thread-0 TestSynchronizedBlock: 4 Thread-0 TestSynchronizedBlock: 5 Thread-0 TestSynchronizedBlock: 6 thread-0 testsy. TestSynchronizedBlock: 9
استخدم كائنات الفئة للتحكم في التزامن بين حالات مختلفة:
الفئة العامة SynchronizedTest2 يمتد Thread {private void testsynchronizedBlock () {synchronized (synchronizedt.class) {for (int i = 0 ؛ i <10 ؛ i ++) {system.out.println (thread.currentTrathread (). getName () + "testsynchronized: حاول {thread.sleep (100) ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ }}}} Override public void run () {testSyNchronizedBlock () ؛ } public static void main (string [] args) {thread t = new SynchronizedTest2 () ؛ T.Start () ؛ الموضوع T2 = new SynchronizedTest2 () ؛ t2.start () ؛ }}نتيجة الإخراج:
Thread-0 TestSynchronizedBlock: 0 Thread-0 TestSynchronizedBlock: 1 Thread-0 TestSynchronizedBlock: 2 Thread-0 TestSynchronizedBlock: 3 Thread-0 TestSynchronized-Plock: 4 TrassSynchronizedBlock: 7 TrassSynchronizedblock: Thread-0 TestSynchronizedBlock: 9 Thread-1 testsynchronizedBlock: 0 Thread-1 testsynchronizedBlock: 1 thread-1 testsynchronizedBlock: 2 thread-1 testsynchronizedblock TestSynchronizedBlock: 8 Thread-1 TestSynchronizedBlock: 9
عند استخدام الكلمة الرئيسية المتزامنة للتحكم في المزامنة ، يجب عليك فهم شاشة الكائن. فقط العملية التي تحصل على الشاشة التي يمكن تشغيلها ، ويجب انتظار كل شيء آخر للحصول على الشاشة. يمكن استخدام أي كائن غير فني كمراقبة كائن. عندما يتم تزامنه على طريقة ما ، يتم قفل مثيل الكائن ؛ عند التصرف على طريقة ثابتة ، يتم قفل مثيل الكائن المقابل للكائن.
طريقة متزامنة لاثنين من الموضوعات للوصول إلى كائن في نفس الوقت
عندما يصل اثنين من المواضيع المتزامنة إلى الطريقة المتزامنة لنفس الكائن ، يمكن تنفيذ مؤشر ترابط واحد فقط. يجب أن ينتظر مؤشر ترابط آخر حتى يتم تنفيذ هذا الموضوع الحالي قبل تنفيذه.
الفئة العامة twothread {public static void main (string [] args) {final twothread twothread = new twothread () ؛ Thread T1 = New Thread (New RunNable () {public void run () {twothread.syncmethod () ؛}} ، "a") ؛ Thread T2 = New Thread (New RunNable () {public void run () {twothread.syncmethod () ؛}} ، "b") ؛ t1.start () ؛ t2.start () ؛ } syncmethod syncmethod () {for (int i = 0 ؛ i <5 ؛ i ++) {system.out.println (thread.currentThRead (). getName () + ":" + i) ؛ حاول {thread.sleep (500) ؛ } catch (InterruptedException IE) {}}}}نتيجة الإخراج:
A: 0A: 1A: 2A: 3A: 4B: 0B: 1B: 2B: 3B: 4
يتم الوصول إلى طريقة التزامن لكائنين
في هذه الحالة ، لا يعمل المزامنة ، تمامًا مثل الطريقة العادية. لأن الأقفال المقابلة هي كائناتها المعنية.
الفئة العامة TwoObject {public static void main (string [] args) {FinalObject Object1 = newObject () ؛ Thread T1 = New Thread (New RunNable () {public void run () {object1.syncmethod () ؛}} ، "Object1") ؛ t1.start () ؛ اثنين من كائن اثنين من كائن 2 = جديد twoObject () ؛ Thread T2 = New Thread (New RunNable () {public void run () {public void run () {object2.syncmethod () ؛}} ، "Object2") ؛ t2.start () ؛ } syncmethod syncmethod () {for (int i = 0 ؛ i <5 ؛ i ++) {system.out.println (thread.currentThRead (). getName () + ":" + i) ؛ حاول {thread.sleep (500) ؛ } catch (InterruptedException IE) {}}}}أحد المخرجات الممكنة:
Object2: 0Object1: 0Object1: 1Object2: 1Object2: 2Object1: 2Object2: 3Object1: 3Object1: 4Object2: 4
يتوصل الخيطان إلى الطريقة الثابتة المتزامنة
في هذه الحالة ، نظرًا لأن الفصل مغلق ، في أي وقت ، يمكن لخيط واحد فقط تنفيذ الطريقة الثابتة.
الوصول إلى الطرق المتزامنة والطرق غير المتزامنة في وقت واحد عندما يصل مؤشر ترابط واحد إلى طريقة تزامن واحد لكائن ما ، لا يزال بإمكان مؤشر ترابط آخر الوصول إلى الطرق غير المتزامنة في هذا الكائن.
الفئة العامة syncandnosync {public static void main (string [] args) {final syncandnosync syncandnosync = new syncandnosync () ؛ Thread T1 = New Thread (new RunNable () {public void run () {syncandnosync.syncmethod () ؛}} ، "a") ؛ t1.start () ؛ Thread T2 = New Thread (New RunNable () {public void run () {syncandnosync.nosyncmethod () ؛}} ، "b") ؛ t2.start () ؛ } syncmethod syncmethod () {for (int i = 0 ؛ i <5 ؛ i ++) {system.out.println (thread.currentThread (). getName () + "في syncmethod ():" + i) ؛ حاول {thread.sleep (500) ؛ } catch (interruptedException ie) {}}} public void nosyncmethod () {for (int i = 0 ؛ i <5 ؛ i ++) {system.out.println (thread.currentThRead (). getName () + "at nosyncmethod ():" + i) ؛ حاول {thread.sleep (500) ؛ } catch (InterruptedException IE) {}}}}إخراج واحد ممكن:
B في nosyncmethod (): 0a في syncmethod (): 0b في nosyncmethod (): 1a في syncmethod (): 1b في nosyncmethod (): 2a في syncmethod (): 2b في nosyncmethod (): 3a في syncmethod (): 3a at syncmeth nosyncmethod (): 4
طرق التزامن مختلفة للوصول إلى نفس الكائن
عندما يصل مؤشر ترابط إلى طريقة التزامن A لكائن ما ، سيتم حظر مؤشرات الترابط الأخرى إلى جميع طرق التزامن الأخرى في الكائن. نظرًا لأن مؤشر الترابط الأول قد حصل على قفل الكائن وغيره من مؤشرات الترابط لا يمكن الحصول على القفل ، على الرغم من أنه يصل إلى طريقة مختلفة ، إلا أنه لا يحصل على القفل ولا يمكنه الوصول إليه.
الفئة العامة twosyncmethod {public static void main (string [] args) {Final twosyncmethod twosyncmethod = new twosyncmethod () ؛ Thread T1 = New Thread (new RunNable () {public void run () {twosyncmethod.syncmethod1 () ؛}} ، "a") ؛ t1.start () ؛ Thread T2 = New Thread (New RunNable () {public void run () {twosyncmethod.syncmethod2 () ؛}} ، "b") ؛ t2.start () ؛ } syncmethod1 () {for (int i = 0 ؛ i <5 ؛ i ++) {system.out.println (thread.currentThread (). getName () + "في syncmethod1 ():" + i) ؛ حاول {thread.sleep (500) ؛ } catch (interruptedException ie) {}}} synchronized void syncmethod2 () {for (int i = 0 ؛ i <5 ؛ i ++) {system.out.println (thread.currentThRead (). getName () + "at syncmethod2 ():" + i) ؛ حاول {thread.sleep (500) ؛ } catch (InterruptedException IE) {}}}}نتيجة الإخراج:
A at syncmethod1 (): 0a at syncmethod1 (): 1a at syncmethod1 (): 2a at syncmethod1 (): 3a at syncmethod1 (): 4b at syncmethod2 (): 0b at syncmethod2 (): 1b at syncmethod2 () syncmethod2 (): 4