2016-10-15 4 views
1

ich Singletons Thread-Klasse haben, die manchmal Funktion aufruft unten und benachrichtigt Zuhörer von ihm Thread run() Methode ist:Legendes Anmeldern auf Zuhörer synchronisiert

public class Serial implements Runnable 
{ 
    private ArrayList observers = new ArrayList(); 
    ... 

    public void run() 
    { 
    notifyListeners(new CS()); 
    } 

    public synchronized void notifyListeners(CS value) 
    { 
     log.debug("notifying listeners with Control "); 
     int os = observers.size(); 
     for (int i = 0; i < observers.size(); i++) 
      { 
       MListener observer = (MListener) observers.get(i); 
       observer.dataReceived(value); 
      } 
    } 
    ... 

    public void addListener(MListener lsn) 
    { 
    observers.add(lsn); 
    } 

    public void removeListener(MListener lsn) 
    { 
    observers.remove(lsn); 
    } 


} 

Ich frage mich nur, was synchronized auf notifyListeners Methode gibt? Einer der Gründe - nicht erlauben hinzufügen/entfernen Beobachter von/zu ArrayList observers während notifyListeners aufgerufen wird. Bitte korrigieren Sie mich, falls ich falsch liege. Was könnte es noch geben?

UPD

Ich habe meinen Code mit zwei Methoden aktualisiert addListener und removeListener. Ich nehme an, es ist ein Fehler, da beide Methoden nicht synchronized sind und von einem anderen Thread aufgerufen werden könnten?

+0

Ich empfehle, das [Multicaster-Muster] (https://docs.oracle.com/javase/8/docs/api/java/awt/AWTEventMulticaster.html) zu betrachten, das die Nichteinmischung während der Ereignislieferung ohne die Sie müssen die Listener-Liste kopieren oder die gesamte Operation synchronisieren. Es ist viel effizienter, besonders für eine kleine Anzahl von Zuhörern. Schauen Sie sich [den Quellcode] (http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/awt/AWTEventMulticaster.java#AWTEventMulticaster) an, um herauszufinden, wie man es für andere Event-/Listenertypen implementiert ... – Holger

Antwort

0

Die Antwort meist auf Ihrem Kontext abhängig, weil es für zwei mögliche Gründe sein könnten:

  1. Wir möchten, dass Ihre Liste der Zuhörer aus gleichzeitige Zugriffe und Änderungen schützen, weil es sich um eine ArrayList ist, das nicht ist thread-sicher (vorausgesetzt, dass die Liste an anderer Stelle in Ihrem Code geändert werden kann und jedes Mal, wenn sie geändert wird, ist sie durch einen synchronized Block mit this als Objektmonitor geschützt, andernfalls würde sie nicht richtig ausgeführt und/oder wäre nutzlos).
  2. Wir möchten gleichzeitige Benachrichtigungen für einige interne Logik zu verhindern.

In Ihrem Fall, wie Sie nur einen Thread haben, die notifyListeners # 2 Anrufe nicht der Grund sein sollen, wenn der Code Eigentümer angenommen, dass Sie mehrere Threads zu tun, diese Aufgabe in der Zukunft haben könnten.

Der Grund # 1 macht nur Sinn, wenn die Liste der Beobachter gleichzeitig von anderen Threads geändert werden kann. Wenn dies nicht möglich ist, können Sie einfach das Schlüsselwort synchronized aus Ihrer Methodendeklaration entfernen, da dies die Leistung beeinträchtigen würde für nichts.

dass # 1 Unter der Annahme, ist der Grund, Sie lieber den Thread-sichere ListCopyOnWriteArrayList anstelle ein ArrayList verwenden sollen, weil es in meist Lesezugriffe Szenarien sehr effizient ist der in der Regel der Fall einer Liste von Beobachtern, die wir meist gelesen und nur selten ändern, würde der Code dann so etwas:

public class Serial implements Runnable { 
    private final List<MListener> observers = new CopyOnWriteArrayList<>(); 
    ... 
    public void notifyListeners(CS value) { 
     log.debug("notifying listeners with Control "); 
     for (MListener observer : observers) { 
      observer.dataReceived(value); 
     } 
    } 

ich nehme an, es Fehler ist, da beide Methoden sind nicht synchronized und könnte ca sein aus einem anderen Thread aufgeflogen?

Ich bestätige, dass der aktuelle Code nicht korrekt ist, da die Liste der observers kann durch gleichzeitige Threads dank der public Methoden addListener und removeListener und diese Methoden nicht ändern Ihre Liste mit this innerhalb eines synchronized Block modifiziert werden, wie Object Monitor, so dass der aktuelle Code ist nicht thread-sicher, da es nicht verhindert gleichzeitige Zugriffe auf Ihre nicht thread-sichere Liste.

+0

Ich habe meinen Code mit zwei Methoden aktualisiert: addListener und removeListener. Ich nehme an, es ist ein Fehler, da beide Methoden nicht synchronisiert sind und von einem anderen Thread aufgerufen werden können. – vico

1

IMO die Synchronisation auf Notify macht keinen Sinn. Wenn ich Sie richtig verstehe, wird die Notify-Methode nur von Ihrem Singleton-Thread aufgerufen.

Der Inhalt Ihrer Observer-Implementierungen kann jedoch wahrscheinlich von verschiedenen Threads aufgerufen werden. Dort muss der Zugriff auf den internen Zustand synchronisiert sein.

z. Wenn Sie den angegebenen Wert für ein Mitglied des Beobachters speichern möchten, das später z.

// called by your notify thread 
void dataReceived(CS value) 
{ 
    synchronized (this) 
    { 
     myValue = value; 
    } 
} 

und:

// called by your GUI main thread: 
public CS getValue() 
{ 
    synchronized (this) 
    { 
     // optional check for not null: 
     if (myValue == null) throw new IllegalStateException(); 
     logger.debug("returning value: " + myValue); 
     return myValue; 
    } 
} 

Wenn der CS ist eine AtomicXY (z Atomicinteger) Klasse, die Synchronisation ist nicht erforderlich das Haupt GUI-Thread Sie den Zugriff auf dieses Mitglied synchronisieren muß. Wenn Sie jedoch mehr tun möchten, als nur den Wert zuzuweisen/zurückzugeben (z. B. eine Überprüfung oder eine Protokollausgabe), ist die Synchronisierung obligatorisch.

+0

Natürlich hast du recht. Ich tippte das hier ohne Unterstützung einer IDE direkt ein. Ich werde es korrigieren. – Heri

+0

Warum würdest du es so schreiben? Das Synchronisieren der Methodendeklaration ist identisch mit dem, wie Sie es haben, außer es wird in der Methodensignatur angezeigt. – matt

+0

@matt: Sie haben Recht, in diesem Beispiel würde es den gleichen Effekt haben. Zwei Gründe dafür: 1. Vielleicht hast du anderen Code in der Methode, der nicht synchronisiert werden muss. 2. Statt dessen können Sie einen anderen Monitor verwenden, der speziell für die Synchronisierung des Zugriffs auf das myValue-Mitglied verwendet wird (z. B. private myValueAccessSemaphore = new Object()). Und man könnte argumentieren, dass auch die Tatsache, die Sie erwähnten ("in der Methodensignatur auftauchen"), ein weiterer Grund dafür ist, sie nicht zu verwenden. In der Tat wollen Sie den Zugriff auf myValue und nicht eine ganze Methode synchronisieren. – Heri