Collections.synchronizedList und synchronisiert

List list = Collections.synchronizedList(new ArrayList()); synchronized (list) { list.add("message"); } 

Ist der Block “synchronisiert (Liste) {}” wirklich nötig?

Sie müssen nicht wie in Ihrem Beispiel synchronisieren. Wie auch immer, sehr wichtig, Sie müssen die Liste um die Liste synchronisieren, wenn Sie sie iterieren (wie im Javadoc angegeben):

Es ist zwingend erforderlich, dass der Benutzer bei der Iteration manuell die zurückgegebene Liste synchronisiert:

 List list = Collections.synchronizedList(new ArrayList()); ... synchronized(list) { Iterator i = list.iterator(); // Must be in synchronized block while (i.hasNext()) foo(i.next()); } 

Es hängt vom genauen Inhalt des synchronized Blocks ab:

  1. Wenn der Block eine einzelne atomare Operation auf der Liste ausführt (wie in Ihrem Beispiel), ist das synchronized überflüssig.

  2. Wenn der Block mehrere Operationen in der Liste ausführt und die Sperre für die Dauer der zusammengesetzten Operation beibehalten muss, ist die synchronized nicht überflüssig. Ein häufiges Beispiel hierfür ist das Iterieren über die Liste.

Der zugrunde liegende Code für die adds-Methode Collections.synchronizedList ist:

 public void add(int index, E element) { synchronized (mutex) {list.add(index, element);} } 

In Ihrem Beispiel ist es also nicht nötig, eine Synchronisation hinzuzufügen.

Beachten Sie auch, dass alle Methoden, die Iteratoren verwenden, beispielsweise Collections.sort (), in einem synchronisierten Block eingeschlossen werden müssen.

Lesen Sie diesen Oracle Doc

Es heißt “Es ist zwingend erforderlich, dass der Benutzer manuell auf der zurückgegebenen Liste synchronisiert, wenn es darüber iteriert”

Wie bereits erwähnt, sind die synchronisierten Sammlungen threadsicher , aber die zusammengesetzten Aktionen für diese Sammlungen sind nicht garantiert threadsicher.

Laut JCIP können die üblichen zusammengesetzten Aktionen sein

  • Iteration
  • Navigation
  • Put-wenn-abwesend
  • checken-dann-handeln

Der synchronisierte Codeblock des OP ist keine zusammengesetzte Aktion, also kein Unterschied, ob es hinzugefügt wird oder nicht.

Nehmen wir das Beispiel von JCIP und modifizieren es etwas, um zu verdeutlichen, warum es notwendig ist, die zusammengesetzten Aktionen mit Sperre zu schützen.

Es gibt zwei Methoden, die auf derselben list , die von Collections.synchronizedList umschlossen wird

 public Object getLast(List list){ int lastIndex = list.size() - 1; return list.get(lastIndex); } public void deleteLast(List list){ int lastIndex = list.size() - 1; list.remove(lastIndex); } 

Wenn die Methoden getLast und deleteLast gleichzeitig von zwei verschiedenen Threads getLast werden, können Interleaves auftreten und getLast ArrayIndexOutOfBoundsException . Angenommen, der aktuelle lastIndex ist 10.

Thread A (deleteLast) -> entfernen
Thread B (getLast) ——————–> erhalten

Der Thread A remove das Element vor der get Operation in Thread B. Daher verwendet der Thread B immer noch 10 als lastIndex , um list.get Methode lastIndex aufzurufen, list.get zu einem gleichzeitigen Problem führen wird.