يدرس هذه المقالة أساسًا المحتوى المتعلق بمشاكل ABA وتجنب في Java ، على النحو التالي.
في الفصل 15 من كتاب "ممارسة Java Concurrency العملية" ، هناك كومة تزامن يتم تنفيذها باستخدام المتغيرات الذرية ، والرمز كما يلي:
عقدة الفئة العامة {عنصر السلسلة النهائية العامة ؛ العقدة العامة التالية ؛ العقدة العامة (عنصر السلسلة) {this.item = item ؛}} الفئة العامة ConcurrentStack {AtomIcReference <Node> TOP = New AtomicReference <Node> () ؛ Public Void Push (عنصر السلسلة) {Node NewTop = new Node (item) ؛ Node Oldtop ؛ pop () {node newtop ؛ node oldtop ؛ do {oldtop = top.get () ؛ if (oldtop == null) {return null ؛} newtop = oldtop.next ؛} بينما (! top.compareandset (oldtop ، newtop)) ؛ إعادة oldtop.item ؛}}}هذا المثال لن يسبب مشاكل ABA. أما لماذا لا ، سأشرح ذلك لاحقًا. دعونا نتحدث عن مشاكل ABA أولاً.
ما هو أبا؟
اقتبس من الكتاب الأصلي: إذا كان يمكن استخدام العقد في الخوارزمية دوريًا ، فقد تحدث هذه المشكلة عند استخدام تعليمات "المقارنة والتبادل". في عملية CAS ، سيتم الحكم على أن "قيمة V لا تزال A؟" ، وإذا كان الأمر كذلك ، فستستمر عملية التحديث. في بعض الخوارزميات ، إذا تغيرت قيمة V أولاً من A إلى B ومن ثم من B إلى A ، فسيعمل CAS بنجاح.
أمثلة على ABA
في بعض الأحيان ، تكون العواقب الناجمة عن ABA خطيرة للغاية. دعنا نعدل مثال مكدس التزامن لمعرفة المشكلات التي ستحدثها ABA:
عقدة الفئة العامة {عنصر السلسلة النهائية العامة ؛ العقدة العامة التالية ؛ العقدة العامة (عنصر السلسلة) {this.item = item ؛}} concurrentStack الفئة العامة {AtomIcReference <Node> top = new AtomIcReference <Node> () ؛ public void push (node node) {node oldtop ؛ do {oldtop = top.get () ؛ node.next = oldtop ؛} بينما oldtop ؛ do {oldtop = top.get () ؛ if (oldtop == null) {return null ؛} newtop = oldtop.next ؛ timeUnit.Seconds.sleep (time) ؛} بينما (! top.compareanset (oldtop ، newtop)) ؛ العودة القديمة ؛}}}}انتبه إلى التغييرات هنا ، لم تتغير العقدة بشكل أساسي
ركز على التغييرات في ConversStack
1. طريقة الدفع: في الأصل ، باستخدام المحتوى لبناء العقدة ، ولكن الآن تمرير مباشرة في العقدة ، والتي تلبي متطلبات "العقد في الخوارزمية يمكن إعادة تدويرها"
2. نوم طريقة البوب ، التي تحاكي تنفيذ الخيط من أجل مراقبة النتائج.
دعنا نضغط أولاً على عقدتين في المكدس:
concurrentStack stack = New ConversStack () ؛ Stack.push (عقدة جديدة ("A")) ؛ Stack.push (عقدة جديدة ("B")) ؛ثم قم بإنشاء موضوعين لأداء العمليات التي تدخل وترك المكدس
الخيط الأول ينفذ التراص: دع ندوة الخروج من المكدس
Stack.pop (3) ؛
لسبب ما ، تم تنفيذ الموضوع A لفترة طويلة واستخدم 3 ثوان
يقوم الخيط B بتنفيذ المكدس ثم يدخل المكدس: أولاً ، يتم إصدار Nodea و Nodeb ، ثم تدع NodeC و NODEC و NODEA (Nodea في الجزء العلوي من المكدس)
العقدة A = stack.pop (0) ؛ Stack.pop (0) ؛ Stack.push (عقدة جديدة ("D")) ؛ Stack.push (عقدة جديدة ("C")) ؛ stack.push (a) ؛ملاحظة: يقوم الموضوع B بتنفيذ إعادة تدوير العقد. يطلق أولاً جميع المحتويات في المكدس ، ثم يضعها في المكدس. أخيرًا ، يكون المحتوى الموجود في الجزء العلوي من المكدس هو العقدة التي تم إصدارها من قبل.
بعد أن قام الموضوع B بتنفيذ هذه الإجراءات ، يقوم مؤشر الترابط A بتنفيذ CAS. في هذا الوقت ، يمكن لـ CAS تنفيذها بنجاح.
وفقًا للفكرة الأصلية ، بعد تنفيذ المواضيع A و B ، يجب أن يكون محتوى المكدس: C و D ، C في الجزء العلوي من المكدس ، ولكن نتيجة التنفيذ هنا هي أنه لا يوجد شيء في Stack ، وهي مشكلة ABA.
كيفية تجنب مشاكل ABA
يتم توفير AtomicStampedReference و AtomicMarkablereference في Java لحل مشاكل ABA
يمكن لـ AtomicStampedReference تحديث قيمتين من الناحية الذرية: مرجع ورقم الإصدار ، ويميز استخدام دورة العقدة حسب رقم الإصدار. دعونا نرى مثال AtomicStampedReference:
ConcurrentStack الفئة العامة {AtomIcStampedReference <Node> TOP = New AtomIcStampedReference <Node> (null ، 0) ؛ public void push (node node) {node oldtop ؛ int v ؛ do {v = top.getStamp () ؛ oldtop = top.getReference () ؛ node.next = Node ، V ، V+1)) ؛ //} بينما (! Top.compareAnset (Oldtop ، Node ، Top.getStamp () ، top.getStamp ()+1)) ؛} العقدة العامة pop (int time) {node newtop ؛ node oldtop ؛ int v ؛ do {v = top.getStamp () ؛ null ؛} newtop = oldtop.next ؛ جرب {timeUnit.seconds.sleep (time) ؛} catch (InterruptedException e) {E.PrintStackTrace () ؛}} بينما (! Newtop ، top.getStamp () ، top.getStamp ())) ؛ return oldtop ؛} public void get () {node node = top.getReference () ؛ بينما (Node! = null) {system.out.println (node.getitem ()) ؛ node = node.getnode () ؛}}}ملاحظة: لا يمكنك استخدام طريقة التعليق ، وإلا فلن يكون مختلفًا عن مجرد استخدام المتغيرات الذرية.
يمكن لـ AtomIcMarkablereference تحديث البتات والأنواع المرجعية من نوع Boolean ، انظر المثال التالي:
AtomIcMarkAblerEference <Node> TOP = new AtomIcMarkAllerEference <Node> (null ، true) ؛ public void push (node node) {node oldtop ؛ boolean v ؛ do {v = top.ismarked () ؛ oldtop = top.getReference () ؛ node.next = oldtop ؛لخص
ما سبق هو كل محتوى هذه المقالة حول مناقشة موجزة حول مشاكل ABA وتجنب في Java. آمل أن يكون ذلك مفيدًا للجميع. يمكن للأصدقاء المهتمين الاستمرار في الرجوع إلى الموضوعات الأخرى ذات الصلة على هذا الموقع. إذا كانت هناك أي أوجه قصور ، فيرجى ترك رسالة لإشارةها. شكرا لك يا أصدقائك لدعمكم لهذا الموقع!