سؤال

لا أفهم لماذا أحصل على concurrentModificationException عندما أتكرر من خلال هذا multimap. قرأت ما يلي دخول, ، لكنني لست متأكدًا مما إذا فهمت كل شيء. حاولت إضافة كتلة متزامنة. لكن شكى هو ما أتزامن معه ، ومتى.

ال multimap هو حقل وإنشاء مثل هذا:

private Multimap<GenericEvent, Command> eventMultiMap =   
   Multimaps.synchronizedMultimap(HashMultimap.<GenericEvent, Command> create());

وتستخدم مثل هذا:

eventMultiMap.put(event, command);

ومثل هذا (حاولت مزامنة هذا الجزء على الخريطة ، ولكن دون نجاح)

for (Entry<GenericEvent, Command> entry : eventMultiMap.entries()) {
    if (entry.getValue().equals(command)) {
        eventMultiMap.remove(entry.getKey(), entry.getValue());
        nbRemoved++;
    }
}
هل كانت مفيدة؟

المحلول

في Java8 ، يمكنك أيضًا استخدام نهج Lambda:

eventMultiMap.entries().removeIf(genericEventCommandEntry -> genericEventCommandEntry.getValue().equals(command));

نصائح أخرى

إن استدعاء إزالة على مجموعة أثناء التكرار من خلالها سيؤدي إلى حدوث متزامن في كل مرة ، حتى لو تم كل شيء في نفس الخيط - الشيء الصحيح الذي يجب القيام به هو الحصول على تكرار صريح واتصال .Remove () على ذلك.

تحرير: تعديل مثالك:

Iterator<Map.Entry<GenericEvent, Command>> i = eventMultiMap.entries().iterator();
while (i.hasNext()) {
    if (i.next().getValue().equals(command)) {
        i.remove();
        nbRemoved++;
    }
}

قد ترغب في رؤية هذا المدونة لموقع آخر يحقق أ ConcurrentModificationException عند عبور تعدد الخريطة ، مع عدم وجود مؤشر ترابط آخر. باختصار ، إذا قمت بالتجاوز مفاتيح Multimap ، والوصول إلى المجموعة المعنية من القيم المرتبطة بكل مفتاح وإزالة بعض العناصر من مثل هذه المجموعة ، إذا صادف أن يكون هذا العنصر هو آخر مجموعة سوف يكون لديك ConcurrentModificationException عندما تحاول الوصول إلى المفتاح التالي - لأن إفراغ مجموعة يؤدي إلى إزالة المفتاح ، وبالتالي تعديل مفاتيح Multimap.

إذا تمكن مؤشر ترابط آخر من تعديل Multimap الخاص بك أثناء تشغيل هذا المنطق ، فستحتاج إلى إضافة كتلة متزامنة إلى رمز Mharris:

synchronized (eventMultimap) {
  Iterator<Entry<GenericEvent, Command>> i = eventMultiMap.entries.iterator();
  while (i.hasNext()) {
    if (i.next().getValue().equals(command)) {
        i.remove();
        nbRemoved++;
    }
  }
}

أو يمكنك حذف التكرار على النحو التالي ،

synchronized (eventMultimap) {
  int oldSize = eventMultimap.size();
  eventMultimap.values().removeAll(Collections.singleton(command));
  nbRemoved = oldSize - eventMultimap.size();
}

لا تتطلب استدعاء removeall () التزامن. ومع ذلك ، إذا قمت بحذف الكتلة المتزامنة ، فيمكن أن تتحول Multimap بين مكالمة removeall () وواحدة من مكالمات Size () ، مما يؤدي إلى قيمة غير صحيحة لـ NBREMOVED.

الآن ، إذا كان الكود الخاص بك متسلسلًا واحدًا ، وكنت ترغب فقط في تجنب مكالمة ConcurrentModificationException ، فيمكنك ترك multimaps.synchronizedmultimap ومنطق متزامن (eventMultImap).

أنا أفضل Multimap.values().iterator() إذا كنت لا تهتم بالمفتاح. يجب أن تحاول أيضًا الابتعاد عن استخدام الكتل المتزامنة قدر الإمكان ، لأنه لا يمكنك إعطاء الأولوية للقراءات/يكتب بشكل فعال.

ReadWriteLock lock = new ReentrantReadWriteLock();
Lock writeLock = lock.writeLock(); 

public void removeCommands(Command value) {
  try {
    writeLock.lock();
    for (Iterator<Command> it = multiMap.values().iterator(); it.hasNext();) {
      if (it.next() == value) {
        it.remove();
      }
    }
  } finally {
    writeLock.unlock();
  }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top