هل توفر حاوية Java تكرارًا آمنًا الفشل
سؤال
ها هي مشكلتي:
هذه القطعة من الكود ترمي أ java.util.ConcurrentModificationException
, ، بسبب ال Vector
listeners
يتم تعديله أثناء وجوده Iterator
لهذا بنية البيانات. يقول Java-Doc أن هذه الحاوية لا توفر سوى موعد فاشل سريع.
هل هناك إمكانية للحصول على Iterator
على حاوية قياسية مثل Vector
أو List
في جافا التي تقدم لي Iterator
, ، لا يصبح هذا غير صالح (ليس سريع الفشل) ، إذا كان هناك عنصر تم إزالته خلال ذلك Iterator
"الأرواح"؟
يجب أن يكون لدي نفس السلوك مثل std::list
في C ++. هناك يكون المكرر صالحًا دائمًا حتى إذا تم إزالة التكرار الحالي. من يتم تعيين التكرار على العنصر التالي في القائمة.
public class ClientHandle {
private final Vector<ClientHandleListener> listeners = new Vector<ClientHandleListener>();
public synchronized void addListener(ClientHandleListener chl) {
listeners.add(chl);
}
public synchronized void removeListener(ClientHandleListener chl) {
listeners.remove(chl);
}
private void fireConnectionClosed() {
final ClientHandle c = this;
final Iterator<ClientHandleListener> it = listeners.iterator();
new Thread(){
@Override
public void run() {
while (it.hasNext()) {
it.next().connectionClosed(c); //FIXME the iterator gets modified
}
};
}.start();
}}
public class ClientHandlePool implements ClientHandleListener, TaskManagerListener {
/*...*/
public synchronized void removeClientHandle(ClientHandle ch) {
//here the listeners Vector from the ClientHandle gets modified
ch.removeListener(this);
ch.removeListener(currentListener);
clientHandles.remove(ch);
}
@Override
public void connectionClosed(ClientHandle ch) {
removeClientHandle(ch);
}
}
المحلول
بقدر ما أعرف أنه لا توجد طريقة لإضافة تلك القدرة بأثر رجعي إلى أي افتراضي Collection
التنفيذ (Iterable
حقيقة).
ولكن هناك تطبيقات تدعم هذا النوع من السلوك من خلال استجابات محددة جيدًا للتعديل المتزامن أثناء التكرار.
مثال واحد هو CopyOnWriteList
.
نصائح أخرى
في حالة المستمعين ، قد تفكر في استخدام java.util.concurrent.CopyOnWriteArrayList
كما يكون لديك عادة المزيد من القراءات من الكتابة.
إلقاء نظرة على حزمة java.util.concurrent ستجد كل ما تحتاجه.
طريقة كسول لإنشاء تكرار سريع آمن من الفشل: خذ نسخة من القائمة كصفيف أثناء قفله ، و foreach () على الصفيف أثناء فتحه ... يمكن القيام به مع أي نوع من القائمة
private void fireConnectionClosed() {
final ClientHandle c = this;
final ClientHandleListener[] listenersArr;
synchronized(this) {
listenersArr=listeners.toArray(new ClientHandleListener[0]);
}
new Thread(){
@Override
public void run() {
for(ClientHandleListener listener : listenersArr )
listener.connectionClosed(c);
}
};
}.start();
}