في هذا الفصل ، سنقدم الكلمات الرئيسية المتزامنة. يتضمن المحتوى المعني:
1. مبدأ متزامن
2. القواعد الأساسية المتزامنة
3. الطريقة المتزامنة وكتلة الشفرة المتزامنة
4. قفل مثيل وقفل عالمي
1. مبدأ متزامن
في Java ، يحتوي كل كائن على قفل التزامن واحد فقط. هذا يعني أيضًا أن قفل المزامنة موجود على الكائن.
عندما نسمي الطريقة المتزامنة لكائن ما ، فإننا نحصل على قفل المزامنة للكائن. على سبيل المثال ، يكتسب Synchronized (OBJ) قفل المزامنة لكائن OBJ ".
إن الوصول إلى أقفال التزامن بواسطة مؤشرات ترابط مختلفة هو حصري بشكل متبادل. بمعنى آخر ، في مرحلة معينة من الوقت ، لا يمكن الحصول على قفل تزامن الكائن إلا بواسطة مؤشر ترابط واحد! من خلال أقفال التزامن ، يمكننا تحقيق الوصول الحصري بشكل متبادل إلى "الكائنات/الأساليب" في مؤشرات ترابط متعددة. على سبيل المثال ، يوجد الآن موضوعان A و Thread B ، والذي يصل كلاهما إلى "قفل متزامن للكائن OBJ". لنفترض أنه في مرحلة ما ، يحصل الخيط A على "قفل مزامنة OBJ" ويؤدي بعض العمليات ؛ يمكن لـ B الحصول على "قفل مزامنة OBJ" فقط حتى يتم تصوير "القفل المتزامن لهذا الكائن" ويمكنه تشغيله فقط.
2. القواعد الأساسية المتزامنة
نلخص القواعد الأساسية للمزامنة في الـ 3 التالية وتوضيحها من خلال أمثلة.
المادة 1: عندما يصل مؤشر ترابط إلى "الطريقة المتزامنة" أو "كتلة رمز متزامنة" من "كائن معين" ، سيتم حظر مؤشرات ترابط أخرى من الوصول إلى "الطريقة المتزامنة" أو "كتلة التعليمات البرمجية المتزامنة" من "الكائن".
المادة 2: عندما يصل مؤشر ترابط إلى "الطريقة المتزامنة" أو "كتلة التعليمات البرمجية المتزامنة" من "كائن معين" ، لا يزال بإمكان مؤشرات الترابط الأخرى الوصول إلى كتلة الكود غير المتزامن من "هذا الكائن".
المادة 3: عندما يصل مؤشر ترابط إلى "الطريقة المتزامنة" أو "كتلة الكود المتزامنة" من "كائن معين" ، سيتم حظر مؤشرات الترابط الأخرى من الوصول إلى "طرق متزامنة" أو "كتلة رمز متزامنة".
المادة 1
عندما يصل مؤشر ترابط إلى "الطريقة المتزامنة" أو "كتلة التعليمات البرمجية المتزامنة" من "كائن معين" ، سيتم حظر مؤشرات الترابط الأخرى من الوصول إلى "الطريقة المتزامنة" أو "كتلة التعليمات البرمجية المتزامنة" من "الكائن".
فيما يلي برنامج العرض التوضيحي المقابل لـ "كتلة الكود المتزامن".
نسخة الكود كما يلي:
فئة myrunable الأدوات runnable {
@تجاوز
تشغيل الفراغ العام () {
متزامن (هذا) {
يحاول {
لـ (int i = 0 ؛ i <5 ؛ i ++) {
thread.sleep (100) ؛
system.out.println (thread.currentThRead (). getName () + "loop" + i) ؛
}
} catch (interruptedException أي) {
}
}
}
}
الفئة العامة demo1_1 {
الفراغ الثابت العام الرئيسي (سلسلة [] args) {
Runnable Demo = new myrunable () ؛
Thind T1 = موضوع جديد (Demo ، "T1") ؛
الموضوع T2 = مؤشر ترابط جديد (Demo ، "T2") ؛
T1.start () ؛
T2.start () ؛
}
}
نتائج التشغيل:
نسخة الكود كما يلي:
حلقة T1 0
T1 حلقة 1
T1 حلقة 2
حلقة T1 3
T1 حلقة 4
T2 حلقة 0
T2 حلقة 1
T2 حلقة 2
T2 حلقة 3
T2 حلقة 4
وصف النتائج:
هناك "كتلة رمز (هذا) متزامن" في طريقة Run () ، و T1 و T2 هي مؤشرات ترابط تم إنشاؤها بناءً على الكائن "التجريبي". هذا يعني أنه يمكننا النظر في هذا في متزامن (هذا) ككائن تجريبي قابل للتشغيل "؛ لذلك ، عند تشغيل مؤشر ترابط واحد ، يجب أن ينتظر مؤشر ترابط آخر حتى يتم إطلاق "مؤشر الترابط الجري" على "قفل المزامنة التجريبية" قبل أن يتمكن من تشغيله.
إذا تأكدت ، فقد اكتشفت هذه المشكلة. ثم نقوم بتعديل الكود أعلاه ، ثم نديره لنرى كيف تكون النتيجة ، ومعرفة ما إذا كنت ستشعر بالارتباك. رمز المصدر المعدل كما يلي:
نسخة الكود كما يلي:
فئة MyThread يمتد الموضوع {
MYTHREAD العام (اسم السلسلة) {
سوبر (الاسم) ؛
}
@تجاوز
تشغيل الفراغ العام () {
متزامن (هذا) {
يحاول {
لـ (int i = 0 ؛ i <5 ؛ i ++) {
thread.sleep (100) ؛
system.out.println (thread.currentThRead (). getName () + "loop" + i) ؛
}
} catch (interruptedException أي) {
}
}
}
}
الفئة العامة demo1_2 {
الفراغ الثابت العام الرئيسي (سلسلة [] args) {
الموضوع T1 = جديد MyTher ("T1") ؛
الموضوع T2 = جديد MyTher ("T2") ؛
T1.start () ؛
T2.start () ؛
}
}
وصف الرمز:
مقارنةً بـ Demo1_2 و Demo1_1 ، وجدنا أن فئة MyThread في Demo1_2 موروثة مباشرة من مؤشر الترابط ، و T1 و T2 كلاهما مؤشر ترابط طفل MyThread.
لحسن الحظ ، فإن طريقة "Run () لـ Demo1_2" تسمى أيضًا Synchronized (هذا) ، تمامًا مثل "طريقة Run () لـ Demo1_1" تسمى أيضًا Synchronized (هذا)!
إذن ، هل عملية تنفيذ Demo1_2 هي نفس عملية تنفيذ Demo1_1؟
نتائج التشغيل:
نسخة الكود كما يلي:
حلقة T1 0
T2 حلقة 0
T1 حلقة 1
T2 حلقة 1
T1 حلقة 2
T2 حلقة 2
حلقة T1 3
T2 حلقة 3
T1 حلقة 4
T2 حلقة 4
وصف النتائج:
إذا كانت هذه النتيجة لا تفاجئك على الإطلاق ، فأعتقد أن لديك فهمًا أعمق للمزامنة وهذا. خلاف ذلك ، يرجى مواصلة قراءة التحليل هنا.
يشير هذا بالتواصل (هذا) إلى "كائن الفئة الحالي" ، أي الكائن الحالي المقابل للفئة التي يوجد فيها متزامن (هذا). الغرض منه هو الحصول على "قفل متزامن للكائن الحالي".
بالنسبة إلى Demo1_2 ، يمثل هذا في كائن MyThered المزامن (هذا) كائن MyThronge ، حيث يتم تنفيذ T1 و T2 المتزامن (هذا) ، حيث يكتسبان أقفال التزامن من كائنات مختلفة. بالنسبة لزوج Demo1_1 ، يمثل هذا في كائن MyRunable ؛
المادة 2
عندما يصل مؤشر ترابط إلى "الطريقة المتزامنة" أو "كتلة التعليمات البرمجية المتزامنة" من "كائن معين" ، لا يزال بإمكان مؤشرات الترابط الأخرى الوصول إلى كتلة التعليمات البرمجية غير المتزامنة من "هذا الكائن".
فيما يلي برنامج العرض التوضيحي المقابل لـ "كتلة الكود المتزامن".
نسخة الكود كما يلي:
عدد الفئة {
// الطريقة التي تحتوي على كتلة المزامنة المتزامنة
Synmethod () public void synmethod () {
متزامن (هذا) {
يحاول {
لـ (int i = 0 ؛ i <5 ؛ i ++) {
thread.sleep (100) ؛
system.out.println (thread.currentThRead (). getName () + "synmethod loop" + i) ؛
}
} catch (interruptedException أي) {
}
}
}
// طريقة غير متزامنة
الفراغ العام nonsynmethod () {
يحاول {
لـ (int i = 0 ؛ i <5 ؛ i ++) {
thread.sleep (100) ؛
System.out.println (thread.currentThRead (). getName () + "nonsynmethod loop" + i) ؛
}
} catch (interruptedException أي) {
}
}
}
الطبقة العامة demo2 {
الفراغ الثابت العام الرئيسي (سلسلة [] args) {
عدد العد النهائي = عدد جديد () ؛
// إنشاء T1 جديد ، سيسمي T1 طريقة Synmethod () من "كائن العد"
الموضوع T1 = موضوع جديد (
new Runnable () {
@تجاوز
تشغيل الفراغ العام () {
count.synmethod () ؛
}
} ، "T1") ؛
// إنشاء T2 جديد ، سوف يدعو T2 طريقة غير المتقنة () من "كائن العد"
الموضوع T2 = موضوع جديد (
new Runnable () {
@تجاوز
تشغيل الفراغ العام () {
count.nonsynmethod () ؛
}
} ، "T2") ؛
T1.start () ؛
T2.start () ؛
}
}
نتائج التشغيل:
نسخة الكود كما يلي:
T1 Synmethod Loop 0
T2 Nonsynmethod Loop 0
T1 Synmethod Loop 1
T2 Nonsynmethod Loop 1
T1 Synmethod Loop 2
T2 Nonsynmethod Loop 2
T1 Synmethod Loop 3
T2 Nonsynmethod Loop 3
T1 Synmethod Loop 4
T2 Nonsynmethod Loop 4
وصف النتائج:
يتم إنشاء خيوط طفل جديدة T1 و T2 في الخيط الرئيسي. سوف تدعو T1 طريقة synmethod () لكائن العد ، والتي تحتوي على كتل المزامنة ؛ عندما يتم تشغيل T1 ، على الرغم من أن المزامنة (هذا) يتم استدعاؤها للحصول على "قفل مزامنة العد" ؛
المادة 3
عندما يصل مؤشر ترابط إلى "الطريقة المتزامنة" أو "كتلة التعليمات البرمجية المتزامنة" من "كائن معين" ، سيتم حظر مؤشرات الترابط الأخرى إلى "طرق متزامنة أخرى" أو "كتلة رمز متزامنة" من "الكائن" ".
سنقوم أيضًا بتعديل الجسم غير المتواصل () في المثال أعلاه مع متزامن (هذا). رمز المصدر المعدل كما يلي:
نسخة الكود كما يلي:
عدد الفئة {
// الطريقة التي تحتوي على كتلة المزامنة المتزامنة
Synmethod () public void synmethod () {
متزامن (هذا) {
يحاول {
لـ (int i = 0 ؛ i <5 ؛ i ++) {
thread.sleep (100) ؛
system.out.println (thread.currentThRead (). getName () + "synmethod loop" + i) ؛
}
} catch (interruptedException أي) {
}
}
}
// يحتوي أيضًا على طريقة كتلة التزامن المتزامنة
الفراغ العام nonsynmethod () {
متزامن (هذا) {
يحاول {
لـ (int i = 0 ؛ i <5 ؛ i ++) {
thread.sleep (100) ؛
System.out.println (thread.currentThRead (). getName () + "nonsynmethod loop" + i) ؛
}
} catch (interruptedException أي) {
}
}
}
}
الطبقة العامة Demo3 {
الفراغ الثابت العام الرئيسي (سلسلة [] args) {
عدد العد النهائي = عدد جديد () ؛
// إنشاء T1 جديد ، سيسمي T1 طريقة Synmethod () من "كائن العد"
الموضوع T1 = موضوع جديد (
new Runnable () {
@تجاوز
تشغيل الفراغ العام () {
count.synmethod () ؛
}
} ، "T1") ؛
// إنشاء T2 جديد ، سوف يدعو T2 طريقة غير المتقنة () من "كائن العد"
الموضوع T2 = موضوع جديد (
new Runnable () {
@تجاوز
تشغيل الفراغ العام () {
count.nonsynmethod () ؛
}
} ، "T2") ؛
T1.start () ؛
T2.start () ؛
}
}
(مرة واحدة) نتيجة التنفيذ:
نسخة الكود كما يلي:
Synmethod (): 11
Synblock (): 3
4. قفل مثيل وقفل عالمي
مثيل قفل-محفور على كائن مثيل. إذا كان الفصل مفردة ، فإن القفل لديه أيضًا مفهوم قفل عالمي.
تتوافق الكلمة الرئيسية المتزامنة مع قفل المثيل.
القفل العالمي- يستهدف هذا القفل فئة.
يتوافق القفل العالمي مع متزامن ثابت (أو مغلق على فئة أو كائن تحميل الفصل في هذه الفئة).
هناك مثال حيوي للغاية على "قفل مثيل" و "قفل عالمي":
نسخة الكود كما يلي:
فئة بولبيك شيء {
issynca () issynca () {}
issyncb () {} void متزامن عام
csynca الثابتة العامة الثابتة () {}
proid csyncb () {} الثابتة الثابتة.
}
لنفترض أن هناك شيئًا ما في حالتان X و Y. تحليل الأقفال التي اكتسبتها مجموعات التعبيرات الأربع التالية.
(01) x.issynca () و x.issyncb ()
(02) x.issynca () و y.issynca ()
(03) x.csynca () و y.csyncb ()
(04) x.issynca () و shote.csynca ()
(01) لا يمكن الوصول إليها في وقت واحد. لأن issynca () و issyncb () كلاهما أقفال المزامنة التي تصل إلى نفس الكائن (الكائن x)!
نسخة الكود كما يلي:
// locktest2.java رمز المصدر
فئة شيء {
issynca () void المتزامن العام () {
يحاول {
لـ (int i = 0 ؛ i <5 ؛ i ++) {
thread.sleep (100) ؛
System.out.println (thread.currentThRead (). getName ()+": issynca") ؛
}
} catch (interruptedException أي) {
}
}
issyncb () {void issyncb () {
يحاول {
لـ (int i = 0 ؛ i <5 ؛ i ++) {
thread.sleep (100) ؛
System.out.println (thread.currentThRead (). getName ()+": issyncb") ؛
}
} catch (interruptedException أي) {
}
}
CSYNCA () {باطل ثابت ثابت
يحاول {
لـ (int i = 0 ؛ i <5 ؛ i ++) {
thread.sleep (100) ؛
system.out.println (thread.currentThRead (). getName ()+": csynca") ؛
}
} catch (interruptedException أي) {
}
}
proid csyncb () {{
يحاول {
لـ (int i = 0 ؛ i <5 ؛ i ++) {
thread.sleep (100) ؛
System.out.println (thread.currentThRead (). getName ()+": csyncb") ؛
}
} catch (interruptedException أي) {
}
}
}
الفئة العامة Locktest2 {
شيء x = شيء جديد () ؛
شيء y = شيء جديد () ؛
// قارن (02) x.issynca () مع y.issynca ()
private void test2 () {
// إنشاء T21 و T21 جديد سيتصل X.issynca ()
الموضوع T21 = موضوع جديد (
new Runnable () {
@تجاوز
تشغيل الفراغ العام () {
x.issynca () ؛
}
} ، "T21") ؛
// إنشاء T22 و T22 جديد سيتصل بـ X.issyncb ()
الموضوع T22 = موضوع جديد (
new Runnable () {
@تجاوز
تشغيل الفراغ العام () {
y.issynca () ؛
}
} ، "T22") ؛
T21.start () ؛
T22.start () ؛
}
الفراغ الثابت العام الرئيسي (سلسلة [] args) {
locktest2 demo = new LockTest2 () ؛
demo.test2 () ؛
}
}
نتائج التشغيل:
نسخة الكود كما يلي:
T11: Issynca
T11: Issynca
T11: Issynca
T11: Issynca
T11: Issynca
T12: Issyncb
T12: Issyncb
T12: Issyncb
T12: Issyncb
T12: Issyncb
(02) يمكن الوصول إليها في نفس الوقت. نظرًا لأنه لا يصل إلى قفل المزامنة لنفس الكائن ، فإن X.issynca () تصل إلى قفل المزامنة لـ x ، بينما يصل y.issynca () إلى قفل التزامن y.
نسخة الكود كما يلي:
// locktest2.java رمز المصدر
فئة شيء {
issynca () void المتزامن العام () {
يحاول {
لـ (int i = 0 ؛ i <5 ؛ i ++) {
thread.sleep (100) ؛
System.out.println (thread.currentThRead (). getName ()+": issynca") ؛
}
} catch (interruptedException أي) {
}
}
issyncb () {void issyncb () {
يحاول {
لـ (int i = 0 ؛ i <5 ؛ i ++) {
thread.sleep (100) ؛
System.out.println (thread.currentThRead (). getName ()+": issyncb") ؛
}
} catch (interruptedException أي) {
}
}
CSYNCA () {باطل ثابت ثابت
يحاول {
لـ (int i = 0 ؛ i <5 ؛ i ++) {
thread.sleep (100) ؛
system.out.println (thread.currentThRead (). getName ()+": csynca") ؛
}
} catch (interruptedException أي) {
}
}
proid csyncb () {{
يحاول {
لـ (int i = 0 ؛ i <5 ؛ i ++) {
thread.sleep (100) ؛
System.out.println (thread.currentThRead (). getName ()+": csyncb") ؛
}
} catch (interruptedException أي) {
}
}
}
الفئة العامة Locktest2 {
شيء x = شيء جديد () ؛
شيء y = شيء جديد () ؛
// قارن (02) x.issynca () مع y.issynca ()
private void test2 () {
// إنشاء T21 و T21 جديد سيتصل X.issynca ()
الموضوع T21 = موضوع جديد (
new Runnable () {
@تجاوز
تشغيل الفراغ العام () {
x.issynca () ؛
}
} ، "T21") ؛
// إنشاء T22 و T22 جديد سيتصل بـ X.issyncb ()
الموضوع T22 = موضوع جديد (
new Runnable () {
@تجاوز
تشغيل الفراغ العام () {
y.issynca () ؛
}
} ، "T22") ؛
T21.start () ؛
T22.start () ؛
}
الفراغ الثابت العام الرئيسي (سلسلة [] args) {
locktest2 demo = new LockTest2 () ؛
demo.test2 () ؛
}
}
نتائج التشغيل:
نسخة الكود كما يلي:
T21: Issynca
T22: Issynca
T21: Issynca
T22: Issynca
T21: Issynca
T22: Issynca
T21: Issynca
T22: Issynca
T21: Issynca
T22: Issynca
(03) لا يمكن الوصول إليها في وقت واحد. نظرًا لأن csynca () و csyncb () كلاهما نوعان ثابتان ، فإن x.csynca () يعادل شيء. sissynca () ، و y.csyncb () يعادل شيء. يطلب في نفس الوقت.
نسخة الكود كما يلي:
// locktest3.java رمز المصدر
فئة شيء {
issynca () void المتزامن العام () {
يحاول {
لـ (int i = 0 ؛ i <5 ؛ i ++) {
thread.sleep (100) ؛
System.out.println (thread.currentThRead (). getName ()+": issynca") ؛
}
} catch (interruptedException أي) {
}
}
issyncb () {void issyncb () {
يحاول {
لـ (int i = 0 ؛ i <5 ؛ i ++) {
thread.sleep (100) ؛
System.out.println (thread.currentThRead (). getName ()+": issyncb") ؛
}
} catch (interruptedException أي) {
}
}
CSYNCA () {باطل ثابت ثابت
يحاول {
لـ (int i = 0 ؛ i <5 ؛ i ++) {
thread.sleep (100) ؛
system.out.println (thread.currentThRead (). getName ()+": csynca") ؛
}
} catch (interruptedException أي) {
}
}
proid csyncb () {{
يحاول {
لـ (int i = 0 ؛ i <5 ؛ i ++) {
thread.sleep (100) ؛
System.out.println (thread.currentThRead (). getName ()+": csyncb") ؛
}
} catch (interruptedException أي) {
}
}
}
فئة عامة Locktest3 {
شيء x = شيء جديد () ؛
شيء y = شيء جديد () ؛
// قارن (03) x.csynca () مع y.csyncb ()
اختبار باطل خاص 3 () {
// إنشاء T31 و T31 جديد سيتصل X.issynca ()
الموضوع T31 = موضوع جديد (
new Runnable () {
@تجاوز
تشغيل الفراغ العام () {
x.csynca () ؛
}
} ، "T31") ؛
// إنشاء T32 جديد ، وسيتصل T32 بـ X.issyncb ()
الموضوع T32 = موضوع جديد (
new Runnable () {
@تجاوز
تشغيل الفراغ العام () {
y.csyncb () ؛
}
} ، "T32") ؛
T31.start () ؛
T32.start () ؛
}
الفراغ الثابت العام الرئيسي (سلسلة [] args) {
locktest3 demo = new LockTest3 () ؛
demo.test3 () ؛
}
}
نتائج التشغيل:
نسخة الكود كما يلي:
T31: Csynca
T31: Csynca
T31: Csynca
T31: Csynca
T31: Csynca
T32: CSYNCB
T32: CSYNCB
T32: CSYNCB
T32: CSYNCB
T32: CSYNCB
(04) يمكن الوصول إليها في وقت واحد. نظرًا لأن Issynca () طريقة مثيل ، فإن X.issynca () يستخدم قفل الكائن X ؛ لذلك ، يمكن الوصول إليها في وقت واحد.
نسخة الكود كما يلي:
// locktest4.java رمز المصدر
فئة شيء {
issynca () void المتزامن العام () {
يحاول {
لـ (int i = 0 ؛ i <5 ؛ i ++) {
thread.sleep (100) ؛
System.out.println (thread.currentThRead (). getName ()+": issynca") ؛
}
} catch (interruptedException أي) {
}
}
issyncb () {void issyncb () {
يحاول {
لـ (int i = 0 ؛ i <5 ؛ i ++) {
thread.sleep (100) ؛
System.out.println (thread.currentThRead (). getName ()+": issyncb") ؛
}
} catch (interruptedException أي) {
}
}
CSYNCA () {باطل ثابت ثابت
يحاول {
لـ (int i = 0 ؛ i <5 ؛ i ++) {
thread.sleep (100) ؛
system.out.println (thread.currentThRead (). getName ()+": csynca") ؛
}
} catch (interruptedException أي) {
}
}
proid csyncb () {{
يحاول {
لـ (int i = 0 ؛ i <5 ؛ i ++) {
thread.sleep (100) ؛
System.out.println (thread.currentThRead (). getName ()+": csyncb") ؛
}
} catch (interruptedException أي) {
}
}
}
الفئة العامة Locktest4 {
شيء x = شيء جديد () ؛
شيء y = شيء جديد () ؛
// قارن (04) x.issynca () مع شيء. csynca ()
private void test4 () {
// إنشاء T41 و T41 جديد سيتصل بـ X.issynca ()
الموضوع T41 = موضوع جديد (
new Runnable () {
@تجاوز
تشغيل الفراغ العام () {
x.issynca () ؛
}
} ، "T41") ؛
// إنشاء T42 و T42 جديد سيتصل بـ X.issyncb ()
الموضوع T42 = موضوع جديد (
new Runnable () {
@تجاوز
تشغيل الفراغ العام () {
شيء. csynca () ؛
}
} ، "T42") ؛
T41.start () ؛
T42.start () ؛
}
الفراغ الثابت العام الرئيسي (سلسلة [] args) {
locktest4 demo = new LockTest4 () ؛
demo.test4 () ؛
}
}
نتائج التشغيل:
نسخة الكود كما يلي:
T41: Issynca
T42: Csynca
T41: Issynca
T42: Csynca
T41: Issynca
T42: Csynca
T41: Issynca
T42: Csynca
T41: Issynca
T42: Csynca