كلمات رئيسية جافا المتطايرة
في معالجة تزامن خيط Java ، هناك الكثير من الالتباس في استخدام كلمة رئيسية متقلبة. أعتقد أن استخدام هذه الكلمة الرئيسية يمكن أن يجعل كل شيء يسير على ما يرام عند استخدام معالجة التزامن متعدد الخيوط.
تدعم لغة جافا متعددة الخيوط. من أجل حل مشكلة تزامن الخيط ، يتم تقديم الكتلة المتزامنة وآلية الكلمات الرئيسية المتطايرة داخل اللغة.
متزامن
الجميع على دراية بالكتل المتزامنة ، ويتم تنفيذها من خلال الكلمة الرئيسية المتزامنة. مع عبارات متزامنة وكتلة ، يمكن لخيط واحد فقط استخدامها في نفس الوقت عند الوصول إلى قوائم متعددة.
طريقة تعديل متزامنة أو كتلة رمز.
متقلب
بالنسبة للمتغيرات التي تم تعديلها باستخدام متطاير ، سيقرأ مؤشر الترابط القيمة الأكثر تعديلًا للمتغير في كل مرة يستخدم فيها المتغير. يتم إساءة استخدام التقلب بسهولة واستخدامها للعمليات الذرية.
دعونا نرى مثالا أدناه. نحن ننفذ عداد. في كل مرة يبدأ فيها مؤشر الترابط ، سيتم استدعاء طريقة Counter Inc لإضافة واحدة إلى العداد.
بيئة التنفيذ - إصدار JDK: JDK1.6.0_31 ، الذاكرة: 3G CPU: x86 2.4g
عداد الفئة العامة {العدد الثابت العام = 0 ؛ Public Static Void Inc () {// التأخير هنا هو 1 مللي ثانية ، مما يجعل النتيجة واضحة المحاولة {thread.sleep (1) ؛ } catch (interruptedException e) {} count ++ ؛ } main static void main (string [] args) {// ابدأ 1000 مؤشر ترابط في نفس الوقت لإجراء حسابات i ++ ورؤية النتيجة الفعلية لـ (int i = 0 ؛ i <1000 ؛ i ++) {new thread (new runnable () {Override public void run () {counter.inc () ؛}}}). start () ؛ } // قد تكون قيمة كل تشغيل هنا مختلفة ، ربما 1000 System.out.println ("Run Running: counter.count =" + counter.count) ؛ }}运行结果:Counter.count= 995
实际运算结果每次可能都不一样,本机的结果为:运行结果:Counter.count= 995 ,可以看出,在多线程的环境下,Counter.count并没有期望结果是1000
يعتقد الكثير من الناس أن هذه مشكلة تزامن متعددة الخيوط. تحتاج فقط إلى إضافة volatile很多人以为,这个是多线程并发问题,只需要在变量count之前加上就可以避免这个问题,那我们在修改代码看看,看看结果是不是符合我们的期望
عداد الطبقة العامة {العدد الثابت العام الثابت = 0 ؛ Public Static Void Inc () {// التأخير هنا هو 1 مللي ثانية ، مما يجعل النتيجة واضحة المحاولة {thread.sleep (1) ؛ } catch (interruptedException e) {} count ++ ؛ } main static void main (string [] args) {// ابدأ 1000 مؤشر ترابط في نفس الوقت ، قم بإجراء حسابات i ++ ، وشاهد النتيجة الفعلية لـ (int i = 0 ؛ i <1000 ؛ i ++) {new thread (new Runnable () {Override public void run () {counter.inc () ؛}}}). start () ؛ } // قد تكون قيمة كل تشغيل هنا مختلفة ، وربما 1000 System.out.println ("Run Running: counter.count =" + counter.count) ؛ }}نتيجة التشغيل: counter.count = 992
لا تزال نتيجة العملية ليست 1000 كما توقعنا. دعونا نحلل الأسباب أدناه
في مقالة مجموعة Java Garbage ، تم وصف تخصيص الذاكرة في لحظة JVM. واحدة من مناطق الذاكرة هي مكدس الجهاز الظاهري JVM ، وكل مؤشر ترابط يحتوي على مكدس مؤشر ترابط عند تشغيله.
يحفظ مكدس مؤشر الترابط معلومات القيمة المتغيرة أثناء وقت تشغيل مؤشرات الترابط. عندما يصل مؤشر ترابط إلى قيمة كائن معين ، ابحث أولاً عن قيمة المتغير المقابل لذاكرة الكومة من خلال مرجع الكائن ، ثم ضع ذاكرة الكومة
يتم تحميل القيمة المحددة للمتغير في الذاكرة المحلية للمعلومات ويتم إنشاء نسخة من المتغير. بعد ذلك ، لم يعد مؤشر الترابط أي علاقة مع القيمة المتغيرة للكائن في ذاكرة الكومة ، ولكنه يعدل مباشرة قيمة متغير النسخ.
في لحظة معينة بعد التعديل (قبل خروج مؤشر الترابط) ، تتم كتابة قيمة نسخة متغير مؤشر الترابط تلقائيًا إلى متغير الكائن في الكومة. بهذه الطريقة ستتغير قيمة الكائن في الكومة. الصورة التالية
صف هذا التفاعل في الكتابة
اقرأ وتحميل متغيرات النسخ من الذاكرة الرئيسية إلى الذاكرة العاملة الحالية
استخدم وتعيين رمز لتغيير القيمة المتغيرة المشتركة
تخزين وكتابة تحديث المحتوى الرئيسي ذي الذاكرة مع بيانات الذاكرة العاملة
حيث يمكن أن يظهر الاستخدام والتعيين عدة مرات
ومع ذلك ، فإن هذه العمليات ليست ذرية ، أي بعد تحميل القراءة ، إذا تم تعديل متغير عدد الذاكرة الرئيسي ، فلن تسبب القيمة في ذاكرة العمل في مؤشر الترابط تغييرات مقابلة لأنه تم تحميله ، لذلك ستكون النتيجة المحسوبة مختلفة عن المتوقع.
بالنسبة للمتغيرات التي تم تعديلها بواسطة متقلبة ، يضمن جهاز JVM الظاهري فقط أن القيمة المحملة من الذاكرة الرئيسية إلى ذاكرة العمل هي الأحدث
على سبيل المثال ، إذا كان مؤشر الترابط 1 و Prohet 2 يقومان بإجراء عمليات القراءة والتحميل ، وابحث عن أن قيمة العد في الذاكرة الرئيسية هي 5 ، فسيتم تحميل أحدث قيمة
بعد تعديل عدد الكومة في الموضوع 1 ، سيتم كتابته في الذاكرة الرئيسية ، وسيصبح متغير العد في الذاكرة الرئيسية 6.
نظرًا لأن Thread 2 قام بالفعل بإجراء عملية القراءة والتحميل ، سيتم أيضًا تحديث القيمة المتغيرة لعدد الذاكرة الرئيسي إلى 6 بعد العملية.
يؤدي هذا إلى حدوث التزامن بعد تعديل خيطين مع الكلمة الرئيسية المتطايرة في الوقت المناسب.
شكرا لك على القراءة ، آمل أن تساعدك. شكرا لك على دعمك لهذا الموقع!