1. السيناريوهات المعمول بها لـ CAS ومزامنة
1. بالنسبة للحالات التي تكون فيها منافسة الموارد أقل ، فإن استخدام قفل المزامنة المتزامن لحظر مؤشرات الترابط وتبديل الاستيقاظ وتبديل عمليات التبديل بين حبات الحالة المستخدم هو مضيعة إضافية لموارد وحدة المعالجة المركزية ؛ في حين يتم تنفيذ CAS استنادًا إلى الأجهزة ، لا تحتاج إلى إدخال kernel ، لا تحتاج إلى تبديل مؤشرات الترابط ، وفرصة تشغيل الدوران أقل ، لذلك يمكن الحصول على أداء أعلى.
2. في حالة المنافسة الخطيرة للموارد ، يكون احتمال تدور CAS مرتفعًا نسبيًا ، مما يضيع المزيد من موارد وحدة المعالجة المركزية وأقل كفاءة من متزامنها. أخذ فئة Atomicinteger في الحزمة java.util.concurrent.atomic كمثال ، يتم تنفيذ طريقة getandincrement () على النحو التالي:
Public Final int getAndIncrement () {for (؛؛) {int current = get () ؛ int التالي = الحالي + 1 ؛ if (CompareAndSet (الحالي ، التالي)) إرجاع التيار ؛ }}إذا تم تنفيذ طريقة المقارنة (الحالية ، التالية) بنجاح ، فسيتم إرجاعها مباشرة ؛ إذا كانت منافسة الخيط شرسة ، مما يؤدي إلى طريقة المقارنة (الحالية ، التالية) التي لا يمكن تنفيذها بنجاح ، فسيتم حلها وانتظرها حتى يتم استنفاد شريحة الوقت المخصصة من قبل وحدة المعالجة المركزية للخيط ، وبالتالي تقليل كفاءة إلى حد كبير.
2. سيناريوهات استخدام خطأ CAS
الفئة العامة Casdemo {private Final int thread_num = 1000 ؛ نهائي خاص int max_value = 20000000 ؛ private Atomicinteger Casi = new AtomicInteger (0) ؛ خاص int synci = 0 ؛ Private String Path = "/Users/Pingping/Datacenter/Books/Linux/Linux Common Orders.txt" ؛ public void casadd () رميات interruptedException {long begin = system.currentTimeMillis () ؛ Thread [] Threads = New Thread [thread_num] ؛ لـ (int i = 0 ؛ i <thread_num ؛ i ++) {threads [i] = new thread (new RunNable () {public void run () {while (casi.get () <max_value) {casi.getandincrement () ؛}}}) ؛ المواضيع [i] .start () ؛ } لـ (int j = 0 ؛ j <thread_num ؛ j ++) {threads [j] .join () ؛ } system.out.println ("تكاليف CAS الوقت:" + (System.CurrentTimeMillis () - start)) ؛ } public void syncadd () رميات interruptedException {long start = system.currentTimeMillis () ؛ Thread [] Threads = New Thread [thread_num] ؛ لـ (int i = 0 ؛ i <froof_num ؛ i ++) {threads [i] = new thread (new RunNable () {public void run () {while (synci <max_value) {synchronized ("synci") {++ synci ؛}}}}}) ؛ المواضيع [i] .start () ؛ } لـ (int j = 0 ؛ j <thread_num ؛ j ++) threads [j] .join () ؛ System.out.println ("تكاليف المزامنة الوقت:" + (system.currentTimeMillis () - start)) ؛ }}تشغيل على وحدة المعالجة المركزية المزدوجة الخاصة بي ، والنتيجة هي كما يلي:
يمكن ملاحظة أنه في ظل خيوط مختلفة ، يكون الوقت الذي يقضيه في استخدام CAS أكثر بكثير من المزامنة. السبب هو السطر 15
14 بينما (casi.get () <max_value) {15 casi.getandincrement () ؛ 16}العملية هي عملية تستغرق وقتًا طويلاً. بعد تنفيذ 15 سطرًا ، سيتم إدخال الحلقة على الفور وسيستمر التنفيذ ، مما يؤدي إلى تعارضات خطيرة في الخيوط.
3. تحسين سيناريوهات استخدام CAS
من أجل حل المشكلة أعلاه ، من الضروري فقط جعل وقت التنفيذ لكل حلقة أطول ، أي أنه يمكن أن يقلل بشكل كبير من تعارضات الخيوط. تعديل الرمز على النحو التالي:
الفئة العامة Casdemo {private Final int thread_num = 1000 ؛ نهائي خاص int max_value = 1000 ؛ private Atomicinteger Casi = new AtomicInteger (0) ؛ خاص int synci = 0 ؛ Private String Path = "/Users/Pingping/Datacenter/Books/Linux/Linux Common Orders Sexpance.txt" ؛ public void casadd2 () رميات interruptedException {long begin = system.currentTimeMillis () ؛ Thread [] Threads = New Thread [thread_num] ؛ لـ (int i = 0 ؛ i <thread_num ؛ i ++) {threads [i] = new thread (new RunNable () {public void run () {while (casi.get () <max_value) {whan (in (in.Read) ؛ (ioException E) {E.PrintStackTrace () ؛ المواضيع [i] .start () ؛ } لـ (int j = 0 ؛ j <thread_num ؛ j ++) threads [j] .join () ؛ System.out.println ("CAS Random Costs الوقت:" + (System.CurrentTimeMillis () - start)) ؛ } public void syncadd2 () رميات interruptedException {long begin = system.currentTimeMillis () ؛ Thread [] Threads = New Thread [thread_num] ؛ لـ (int i = 0 ؛ i <thread_num ؛ i ++) {threads [i] = new thread (new RunNable () {public void run () {while (synci <max_value) {synchronized ("synci") {++ synci ؛} trystream in = new fileInputStree } catch (ioException e) {E.PrintStackTrace () ؛ المواضيع [i] .start () ؛ } لـ (int j = 0 ؛ j <thread_num ؛ j ++) threads [j] .join () ؛ System.out.println ("تكاليف المزامنة الوقت:" + (system.currentTimeMillis () - start)) ؛ }}في حلقة بينما ، تتم إضافة عملية لقراءة محتويات الملف ، والتي تستغرق حوالي 40 مللي ثانية ، وبالتالي تقليل تعارضات الخيوط. نتائج الاختبار كما يلي:
يمكن ملاحظة أنه عندما تكون تعارضات الموارد صغيرة نسبيًا ، تكون طريقة CAS وكفاءة المزامنة المتزامنة متشابهة. لماذا لا تحقق CAS أداءً أعلى من المزامنة؟
JDK المستخدمة في الاختبار هو 1.7. بدءًا من JDK1.6 ، تم إدخال الكثير من التحسينات لتنفيذ الأقفال ، مثل القفل الخشن ، والتخلص من القفل ، وقفل خفيف الوزن ، وقفل متحيز ، والغزل التكيفي وغيرها من التقنيات لتقليل النفقات العامة في عملية القفل. مبدأ قفل الدوران يشبه CAS Spin ، وهو أكثر تحسينًا من CAS Spin. للحصول على التفاصيل ، يرجى الرجوع إلى آلية قفل JVM المتعمقة 1-synchronism.
4. ملخص
1. عند استخدام CAS ، عندما تكون تعارضات الخيط خطيرة ، سيتم تقليل أداء البرنامج بشكل كبير ؛ CAS مناسب فقط للحالات التي يوجد فيها عدد أقل من صراعات الخيوط.
2. تم تحسين المزامنة وتحسينها بعد JDK1.6. يعتمد التنفيذ الأساسي للتزامن بشكل أساسي على قائمة انتظار القفل. الفكرة الأساسية هي الحظر بعد الدوران ، والاستمرار في التنافس على الأقفال بعد تبديل المنافسة ، والتضحية قليلاً بالإنصاف ، ولكن الحصول على إنتاجية عالية. عندما يكون هناك عدد أقل من تعارضات الخيوط ، يمكن الحصول على أداء مماثل ؛ عندما يكون هناك تعارضات خطيرة في الخيط ، يكون الأداء أعلى بكثير من الأداء من CAS.
الملخص أعلاه للبرمجة المتزامنة Java - باستخدام CAS بعناية هو التفسير التفصيلي للمحرر. آمل أن يعطيك مرجعًا وآمل أن تتمكن من دعم wulin.com أكثر.