2012-07-22 17 views
12

Ich habe einige Überraschungen erlebt, wenn es um die Queue-Implementierung für ein Multithreading-System geht. Hier ist: -Synchronisiert vs ReentrantLock auf Leistung

Das Szenario: - 1 Produzent, 1 Verbraucher: - Ein Produzent legt eine ganze Zahl in eine Warteschlange. Ein Verbraucher entfernt ihn einfach aus der Warteschlange.

Die zugrundeliegende Datenstruktur der Warteschlange: - TreeSet (was ich hätte nie gedacht, werde ich verwenden), LinkedList, LinkedBlockingQueue (mit unbestimmter Größe)

der Code: - von TreeSet als Warteschlange: -

while (i < 2000000) { 
     synchronized (objQueue) { 

      if (!(objQueue.size() > 0)) { 
       try { 
        objQueue.wait(); 
       } catch (InterruptedException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 
      } 
      Integer x = objQueue.first(); 
      if (x != null) { 
       objQueue.remove(x); 
       ++i; 
      } 
     } 
    } 

EDIT: -

 while (i < 2000000) { 
     synchronized (objQueue) { 
      objQueue.add(i); 
      ++i; 
      objQueue.notify(); 
     } 
    } 

Für LinkedBlockingQueue: -

 while (i < 2000000){ 
     try { 
      objQueue.put(i); 
      ++i; 
     } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      Thread.currentThread().interrupt(); 
     } 
    } 

     while (i < 2000000) { 
     try { 
      objQueue.take(); 
      ++i; 

     } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      Thread.currentThread().interrupt(); 
     } 
    } 

Für LinkedList: - ähnlicher Code mit synchronisiert.

Die Fragen: -

1) Wenn ich die Leistung über Visuelle VM gemessen, ich festgestellt, dass die für den Erzeugercode, führt TreeSet besser als LinkedBlockingQueue und LinkedList, auch wenn es O (log n) Zeit in Anspruch nimmt Die Erstellung von Objekten in verknüpften Strukturen ist ein erheblicher Aufwand. Warum unterscheidet sich die Theorie von der Praxis? Warum bevorzugen wir Linked, Array-Strukturen gegenüber Tree-Strukturen in Warteschlangenimplementierungen?

2) Das synchronisierte kommt als eindeutiger Gewinner gegenüber dem ReeentrantLock heraus, da TreeSet besser als LinkedList war und eine bessere Performance als LinkedBlockingQueue aufwies. Ich wünschte, ich könnte die Visual VM-Ergebnisse anhängen. Es ist nicht in Stimmen mit dem Artikel, http://www.ibm.com/developerworks/java/library/j-jtp10264/index.html

Die Operationen auf

Dell Vostro 1015 durchgeführt werden, Core 2 Duo 2.10, 2 GB Ram mit 32-Bit-Betriebssystem und mit

JVM: Java HotSpot (TM) Client VM (20,1-b02, mixed mode) Java: Version 1.6.0_26, Anbieter Sun Microsystems Inc.

Antwort

13

1.ReentrantLock könnte passender sein verwenden, wenn Sie einen Thread implementieren müssen, dass Durchläuft eine verknüpfte Liste, sperrt den nächsten Knoten und entsperrt dann den aktuellen Knoten.

2.Synchronized Schlüsselwort ist in Situation wie Verriegelungs Vergröberung apt stellt adaptive Spinnen, vorgespannte Verriegelung und das Potential für eine Sperre elision über Escape-Analyse. Diese Optimierungen werden derzeit nicht für ReentrantLock implementiert.

Für einen richtigen Leistungsvergleich sehen:

http://lycog.com/concurency/performance-reentrantlock-synchronized/

+0

Es ist fast das gleiche Beispiel, für das ich einen Link in meiner Frage anschloss. Es sagt mir, dass die Leistung von REL besser als synchronisiert ist, beginnend mit 2 Threads. Außerdem habe ich das Gefühl, wenn wir einen Weg finden, den OS-Scheduler zu zwingen, die Threads auf allen verfügbaren CPUs auszuführen, werden wir definitiv eine bessere Leistung für LinkedBlockingQueues bekommen. Wenn Sie meine Beobachtungsdaten benötigen, lassen Sie es mich wissen. – 100pipers

+2

Kumar vergaß zu erwähnen, dass er seine Antwort aus [David Dices Weblog] (https://blogs.oracle.com/dave/entry/java_util_concurrent_reentrantlock_vs) nahm. –

+0

@AlexanderRyzhov danke für den Hinweis auf den ursprünglichen Autor dieses Artikels, wie ich dies las, während ich für meine scjp von so anderen Blog vorbereitete ... Vielen Dank –

4
  1. Weil Ihre Benchmark fehlerhaft ist: in einem realen Anwendungsfall wurde die Zeit, Elemente aus der Warteschlange zu produzieren und zu konsumieren ist viel wichtiger als die Zeit, die zum Hinzufügen und Entfernen eines Elements in die Warteschlange benötigt wird. Daher ist die rohe Leistung der Warteschlange nicht so wichtig. Übrigens zeigt der Code nur, wie Sie Elemente aus der ersten Warteschlangenimplementierung übernehmen und nicht wie Sie sie hinzufügen. Darüber hinaus wird die Wahl der geeigneten Struktur nicht auf der Grundlage der Leistung, sondern des Verhaltens getroffen. Wenn Sie etwas gleichzeitig wünschen, wählen Sie eine Blockierungswarteschlange, da diese für Sie implementiert ist und keine Fehler wie Ihr Code aufweist. Wenn Sie FIFO (was Sie oft wollen) wollen, wählen Sie kein TreeSet.

  2. Wenn Sie synchronisierte vs. ReentrantLock vergleichen möchten, sollten Sie keine Datenstruktur für eine und eine andere Datenstruktur für die andere verwenden. ReentrantLock war früher schneller, aber heute sind sie auf dem gleichen Niveau (wenn ich glaube, was Brian Goetz in JCIP sagt). Wie auch immer, ich würde aus Sicherheitsgründen/Fähigkeit eine aus der anderen wählen. Nicht aus Leistungsgründen.

+0

Ich stimme nicht zu, dass die Entscheidungskriterien sollten die Leistung nicht berücksichtigen. Ein typischer Ansatz wäre, die Lösung korrekt zu funktionieren und dann die Leistung zu berücksichtigen. Aber es ist absolut nichts falsch daran, es vorher zu betrachten, da es möglicherweise helfen kann, zeitaufwändiges Refactoring später zu sparen. – Brady

+0

@JB Nizet: - Welchen Fehler enthält mein Code? Ich habe das ähnliche Verständnis, dass ich für FIFO kein TreeSet verwenden sollte, aber sagen Sie mir den Grund, wenn die Put-Methode von TreeSet viel schneller ist als die add-Methode einer LinkedList (weil die Objektaufbauzeit hoch ist), warum würde verwende ich kein TreeSet (obwohl die Entfernung im Falle einer LinkedList sehr schnell ist)? Für die Echtzeit-Trading-Anwendungen zählt nur die Performance. Also der Kern der Geschichte ist, dass ich einen besseren DS für die Warteschlange verwenden möchte und ich REL über synchronisiert verwenden möchte. – 100pipers

+1

@Brady Klar, fang nicht mit einem dummen Design an, das offensichtlich schlecht abschneidet, aber du solltest keine spezifische Implementierung wählen, die auf einer Performance-Ahnung basiert. Wählen Sie diejenige aus, die am besten zu den Anforderungen Ihrer Anwendung passt und Sie weniger Fehler haben. Wenn und nur wenn die Leistung zu einem * Problem * wird, suchen Sie nach Möglichkeiten, es durch Austauschen von Implementierungen zu verbessern. – Bohemian