2010-04-18 7 views
64

Ich lese das Buch Java Concurrency in der Praxis. In Kapitel 15 sprechen sie über die nicht blockierenden Algorithmen und die compare-and-swap-Methode (CAS).Java Concurrency: CAS vs Sperren

Es wird geschrieben, dass CAS viel besser als die Sperrmethoden durchführen. Ich möchte die Leute fragen, die bereits mit diesen beiden Konzepten gearbeitet haben und möchten gerne hören, wann Sie welches dieser Konzepte bevorzugen? Ist es wirklich so viel schneller?

Für mich ist die Verwendung von Sperren viel klarer und einfacher zu verstehen und vielleicht sogar besser zu halten (bitte korrigieren Sie mich, wenn ich falsch bin). Sollten wir uns wirklich darauf konzentrieren, unseren konkurrierenden Code in Bezug auf CAS zu erstellen, anstatt Sperren zu verwenden, um einen besseren Leistungsschub zu erzielen, oder ist Nachhaltigkeit wichtiger?

Ich weiß, es gibt vielleicht keine strenge Regel, wann was zu verwenden ist. Aber ich möchte nur einige Meinungen, Erfahrungen mit dem neuen Konzept von CAS hören.

Antwort

37

CAS ist im Allgemeinen viel schneller als Sperren, aber es hängt vom Grad der Konkurrenz ab. Da CAS einen Wiederholungsversuch erzwingen kann, wenn sich der Wert zwischen Lesen und Vergleichen ändert, kann ein Thread theoretisch in einem busy-wait steckenbleiben, wenn die betreffende Variable von vielen anderen Threads hart getroffen wird (oder wenn es teuer ist, einen neuen Wert zu berechnen) aus dem alten Wert (oder beiden)).

Das Hauptproblem mit CAS ist, dass es viel schwieriger ist, mit korrekt zu programmieren als das Sperren. Wohlgemerkt, Sperren ist wiederum viel schwieriger zu verwenden als Message-Passing oder STM, also nehmen Sie dies nicht als klingelnde Billigung für die Verwendung von Sperren.

+0

Ich stimme zu, setzen Akzent auf * Kosten der Berechnung eines neuen Wertes *. Oft ist es so groß, dass die nicht-lockende Strategie zu lock-based verliert. –

16

Sie können die Zahlen zwischen einer ConcurrentLinkedQueue und einer BlockingQueue betrachten. Was Sie sehen werden, ist, dass CAS unter moderaten (realistischer in realen Anwendungen) Thread-Contention merklich schneller ist. Die attraktivste Eigenschaft von nicht blockierenden Algorithmen ist die Tatsache, dass, wenn ein Thread ausfällt (Cache-Fehltreffer oder schlechter, Seg-Fehler), andere Threads diesen Fehler nicht bemerken und weitermachen können. Wenn jedoch beim Sperren einer Sperre der Thread, der die Sperre hält, eine Art Betriebssystemfehler aufweist, wird jeder andere Thread, der auf die Freigabe der Sperre wartet, ebenfalls mit dem Fehler belegt.

Um Ihre Fragen zu beantworten, können Ja nonblocking thread sichere Algorithmen oder Sammlungen (ConcurrentLinkedQueue, ConcurrentSkipListMap/Set) erheblich schneller als ihre blockierenden Gegenstücke sein. Wie Marcelo jedoch betont hat, ist es sehr schwierig, nicht blockierende Algorithmen zu korrigieren, was viel Aufmerksamkeit erfordert.

Sie sollten über die Michael and Scott Queue lesen, das ist die Warteschlangenimplementierung für ConcurrentLinkedQueue und erläutert, wie man eine zweiwegige, threadsichere, atomare Funktion mit einem einzigen CAS behandelt.

+1

Der Artikel über die "Michael und Scott Queue" ist interessant. Danke! – Prine

12

Es gibt gutes Buch stark auf das Thema schleusenfreien Gleichzeitigkeit bezogen werden: „Die Kunst der Multi-Prozessor-Programmierung“ von Maurice Herlihy

+0

Auch seine Präsentation [Teil 1] (http://research.microsoft.com/apps/video/default.aspx?id=168298) und [Teil 2] (http://research.microsoft.com/apps/video /default.aspx?id=169831) sind sehr lohnenswert. –

23

die relativen Geschwindigkeit der Operationen ist weitgehend kein Thema. Was relevant ist, ist der Unterschied in der Skalierbarkeit zwischen blockbasierten und nicht blockierenden Algorithmen. Und wenn Sie auf einem 1- oder 2-Kern-System laufen, denken Sie über solche Dinge nach.

Nicht blockierende Algorithmen skalieren im Allgemeinen besser, da sie kürzere "kritische Abschnitte" als sperrbasierte Algorithmen haben.

5

Wenn Sie einen echten Vergleich der Welt suchen, hier ist einer. Unsere Anwendung hat zwei (2) Threads 1) Ein Leser-Thread für die Netzwerk-Paketerfassung und 2) ein Consumer-Thread, der das Paket entgegennimmt, zählt und Statistiken ausgibt.

Thread # 1 tauscht ein einzelnes Paket zu einem Zeitpunkt mit Gewinde # 2

Ergebnis # 1 - verwendet eine benutzerdefinierte CAS basierten Austausch den gleichen Prinzipien wie SynchronousQueue mit, wo unsere Klasse aufgerufen wird CASSynchronousQueue:

30,766,538 packets in 59.999 seconds :: 500.763Kpps, 1.115Gbps 0 drops 
libpcap statistics: recv=61,251,128, drop=0(0.0%), ifdrop=0 

Ergebnis # 2 - wenn wir unsere CAS-Implementierung mit dem Standard Java ersetzen S ynchronousQueue:

8,782,647 packets in 59.999 seconds :: 142.950Kpps, 324.957Mbps 0 drops 
libpcap statistics: recv=69,955,666, drop=52,369,516(74.9%), ifdrop=0 

Ich glaube nicht, der Unterschied in der Leistung nicht mehr klarer sein könnte.