Concurrencia de Java: CAS vs Locking
concurrency compare-and-swap (5)
Estoy leyendo el libro Concurrencia de Java en la práctica . En el capítulo 15, están hablando de los algoritmos de no bloqueo y el método de comparar y cambiar (CAS).
Está escrito que CAS funciona mucho mejor que los métodos de bloqueo. Quiero preguntarle a las personas que ya trabajaron con estos dos conceptos y me gustaría saber cuándo está prefiriendo cuál de estos conceptos? ¿Es realmente mucho más rápido?
Para mí, el uso de bloqueos es mucho más claro y más fácil de entender y quizás incluso mejor de mantener (corríjanme si me equivoco) . ¿Deberíamos centrarnos realmente en crear nuestro código simultáneo relacionado con CAS que bloqueos para obtener un mejor impulso de rendimiento o la sostenibilidad es más importante?
Sé que tal vez no haya una regla estricta sobre cuándo usar qué. Pero me gustaría escuchar algunas opiniones, experiencias con el nuevo concepto de CAS.
El CAS generalmente es mucho más rápido que el bloqueo, pero depende del grado de contención. Debido a que CAS puede forzar un reintento si el valor cambia entre leer y comparar, un hilo teóricamente puede atascarse en una espera ocupada si la variable en cuestión está siendo golpeada duramente por muchos otros hilos (o si es costoso calcular un nuevo valor) del valor anterior (o ambos)).
El problema principal con CAS es que es mucho más difícil programar con el bloqueo correctamente. Tenga en cuenta que el bloqueo es, a su vez, mucho más difícil de usar correctamente que el envío de mensajes o STM , por lo que no debe tomar esto como un respaldo para el uso de bloqueos.
Hay un buen libro relacionado con el tema de la concurrencia sin bloqueos: "El arte de la programación multiprocesador" de Maurice Herlihy
La velocidad relativa de las operaciones es, en gran medida, un problema. Lo que es relevante es la diferencia en la escalabilidad entre algoritmos basados en bloqueo y no bloqueo. Y si está ejecutando un sistema central de 1 o 2, deje de pensar en esas cosas.
Los algoritmos sin bloqueo generalmente escalan mejor porque tienen "secciones críticas" más cortas que los algoritmos basados en bloqueo.
Si está buscando una comparación del mundo real, aquí hay una. Nuestra aplicación tiene dos (2) subprocesos 1) Un hilo de lectura para la captura de paquetes de red y 2) un hilo de consumidor que toma el paquete, lo cuenta e informa las estadísticas.
El subproceso n. ° 1 intercambia un solo paquete a la vez con el subproceso n. ° 2
Resultado n. ° 1 : utiliza un intercambio personalizado basado en CAS utilizando los mismos principios que SynchronousQueue , donde nuestra clase se llama 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
Resultado n. ° 2 : cuando reemplazamos nuestra implementación de CAS con el estándar java SynchronousQueue :
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
No creo que la diferencia en el rendimiento no pueda ser más clara.
Puede ver los números entre una ConcurrentLinkedQueue
y una BlockingQueue
. Lo que verán es que CAS es notablemente más rápido en contención de hilos moderada (más realista en aplicaciones del mundo real).
La propiedad más atractiva de los algoritmos no bloqueantes es el hecho de que si falla un hilo (falta de caché, o peor, falla de seg), otros hilos no notarán esta falla y podrán seguir adelante. Sin embargo, cuando se adquiere un bloqueo, si el hilo que contiene el bloqueo tiene algún tipo de falla del sistema operativo, cada hebra que espere a que se libere el bloqueo también será golpeada con el fallo.
Para responder a sus preguntas, sí, los algoritmos o recopilaciones no bloqueables de subprocesos ( ConcurrentLinkedQueue
, ConcurrentSkipListMap/Set
) pueden ser significativamente más rápidos que sus contrapartes de bloqueo. Sin embargo, como señaló Marcelo, corregir los algoritmos que no bloquean es muy difícil y requiere mucha consideración.
Debería leer sobre Michael y Scott Queue , esta es la implementación de colas para ConcurrentLinkedQueue
y explica cómo manejar una función atómica bidireccional y segura para subprocesos con un solo CAS .