Frage zu Synchronisation bei Observer pattern (ConcurrentModificationException)

Billie

Erfahrenes Mitglied
Hellas!

Also, ich möchte nur sichergehen dass ich richtig gehe.

Angenommen wir registrieren einen Listener auf einem Subject, als Beispiel einen Wecker:

Java:
public class AlarmClock extends Thread {

	private List<IWakeUpListener> listeners = null;

	private long sleep = 0L;

	public AlarmClock(final long sleep) {
		this.sleep = sleep;
		listeners = new ArrayList<IWakeUpListener>();
	}

	private void notifyListeners() {
		synchronized (listeners) {
			for (IWakeUpListener listener : listeners) {
				listener.wakeUp();
			}
		}
	}

	public void addWakeUpListener(IWakeUpListener e) {
		synchronized (listeners) {
			listeners.add(e);
		}
	}

	public void removeWakeUpListener(IWakeUpListener e) {
		synchronized (listeners) {
			listeners.remove(e);
		}
	}

	@Override
	public void run() {
		try {
			Thread.sleep(sleep);
		} catch (InterruptedException e) {
		}
		notifyListeners();
	}
}

und eine schlafende Person als Listener die nach einer Sekunde vom Wecker geweckt wird:

Java:
public class SleepingPerson implements IWakeUpListener {

	private AlarmClock clock = null;

	private SleepingPerson() {
		clock = new AlarmClock(1000L);
		clock.start();
		clock.addWakeUpListener(this);

	}

	@Override
	public void wakeUp() {
		System.out.println("I'm awake");
		clock.removeWakeUpListener(this);
	}

	public final static void main(String args[]) {
		SleepingPerson person = new SleepingPerson();

	}
}

Also der AlarmClock-Thread ist der selbe Thread der die schlafende Person weckt (wakeUp()) und gleichzeitig der Thread, der den Listener wieder entfernt.

Wird jetzt die Klasse SleepingPerson über die main-Methode gestartet, bekommen wir eine ConcurrentModificationException obwohl alle Zugriffe auf die Listener in der AlarmClock synchronisiert erfolgen.

Ist es jetzt richtig dass das Problem die foreach-Schleife in AlarmClock.notifyListeners() ist? Um genau zu sein, ist das Problem der Iterator der intern von der foreach-Schleife verwendet wird und durch ein removeWakeUpListener natürlich nicht aktualisiert wird?

Ist somit folgende (funktionierende) Variante eine gute Lösung:

Java:
	private void notifyListeners() {
		synchronized (listeners) {
			for (int i = 0; i < listeners.size(); i++) {
				IWakeUpListener listener = listeners.get(i);
				listener.wakeUp();
			}
		}
	}

Ich nehme an der Zugriff auf die Liste muss trotzdem immer synchronisiert erfolgen, ansonsten gibt es irgendwann eine ArrayIndexOutOfBoundsException?

Beste Grüße,
Billie
 
Zuletzt bearbeitet von einem Moderator:
Dein Problem hat nichts mit dem Observer zu tun!

du arbeitest auf einer liste die einen iterator hat und diesen benutzt und das kann die foreach nicht!

Beispiel:

Die Foreach erzeugt am Anfang einen Size parameter der nicht wieder verändert werden kann, dieser wird mit der listen size befüllt. Und wenn du nun einen wert aus der liste löscht ist die liste die du liest kürzer als die listen größe, die die schleife kennt!
bei den Schleifen Durchläufen würdest du irgendwann ans ende kommen wobei die schleife weiter lesen will. ArrayOutOfBounds ;) das fängt die schleife rechtzeitig ab, undzwar mit der Exception die du bekommst!

was auch ein sauberes verhalten ist!

Nun müsstet du das problem haben das wenn du einen wert löscht dein counter auf den eigentlich übernächsten wert zeigt und nicht auf den nächsten, da du ja mitten drin einen wert löscht und alle nachfolgenden aufrücken.
 
Zurück