¿Cómo funciona el Disruptor de LMAX? (respuesta de stackoverflow)
Se puede utilizar para reemplazar la cola y tiene muchas características de SEDA y modo Actores. En comparación con las colas: Disruptor puede enviar mensajes a otros subprocesos y activar otros subprocesos cuando sea necesario (similar a BlockingQueue). Sin embargo, existen tres diferencias principales entre ellos. 2. Poner el mensaje en el Disruptor requiere dos pasos. Primero, declarar una interfaz en el búfer de anillo. Esta interfaz proporciona al usuario una entrada que puede almacenar datos correctos. Luego se debe enviar la entrada. Estos dos pasos son necesarios para usar la memoria de manera flexible como se mencionó anteriormente. La operación de confirmación permite que el hilo del consumidor lea el mensaje. 3. El consumidor debe realizar un seguimiento de los mensajes consumidos en el búfer circular. No permitir que el búfer en anillo rastree los mensajes puede reducir la aparición de conflictos de escritura, porque cada subproceso mantiene su propio contador. Comparar con Actors El modelo Actor es el modelo de programa más cercano al Disruptor, especialmente usando la clase BatchConsumer/BatchHandler. Estas clases ocultan toda la complejidad de mantener los números de secuencia consumidos y proporcionan métodos de devolución de llamada simples cuando suceden cosas importantes. Pero hay dos pequeñas diferencias. 1. Disruptor usa un hilo correspondiente a un modelo de consumidor, mientras que Actors usa un modelo de muchos a muchos. Por ejemplo, puede tener tantos actores como sea posible y se distribuirán en una cierta cantidad de hilos (generalmente un actor). por núcleo). 2. La interfaz BatchHandler proporciona un método de devolución de llamada adicional (y muy importante) onEndOfBatch(). Permite que operaciones que requieren mucho tiempo, como operaciones de E/S, se ejecuten juntas en lotes para mejorar el rendimiento. El procesamiento por lotes también se puede realizar utilizando otros marcos de Actor, pero casi ninguno de ellos proporciona un método de devolución de llamada para el final de la ejecución por lotes. Solo puede usar un tiempo de espera para determinar si el procesamiento por lotes ha finalizado, lo que genera una latencia deficiente. En comparación con SEDA, LMAX diseñó el modo Disruptor para reemplazar a SEDA. 1. En comparación con SEDA, la principal mejora del disruptor es que puede funcionar en paralelo. Disruptor admite mensajes de multidifusión para lograr esta funcionalidad, donde el mismo mensaje (en un orden coherente) se envía a varios consumidores. Esto evita el cruce de capas en la tubería. 2. Los consumidores pueden confiar en los resultados del procesamiento de otros consumidores colocando una capa de cola entre ellos. Un consumidor puede simplemente ver los números de serie de los consumidores de los que depende. Esto evita fusionar capas en la tubería. En comparación con las fallas de memoria, el disruptor puede entenderse como una falla de memoria estructurada y ordenada. Entre ellos, el trastorno del productor equivale al trastorno de la escritura y el trastorno del consumidor equivale a la dislexia. Segunda respuesta (respondida el 16 de julio de 2011 a las 5:48, irreputable): Primero, comprendamos el modelo de programación que proporciona. Tiene uno o más autores y lectores. Hay una fila de entradas desde la más antigua hasta la más nueva (de izquierda a derecha). Los autores pueden agregar nuevas entradas a la derecha. Cada lector lee las entradas de izquierda a derecha. Obviamente, los lectores no pueden saltarse al autor y leer primero. Las entradas no se pueden eliminar. Utilizo "lector" en lugar de "consumidor" para evitar la impresión de que se consumirán artículos. Pero sabemos que la entrada a la izquierda del último lector es inútil. Normalmente, los lectores pueden leer las entradas de forma simultánea e independiente. Pero podemos declarar dependencias entre lectores. Puede haber dependencias arbitrarias no circulares entre lectores. Si el lector B depende del lector A, el lector B no puede omitir al lector A y leer primero. El lector A puede anotar un elemento y el lector B depende de esta anotación, por lo que existe una dependencia entre lectores. Por ejemplo, A hace algunos cálculos en una entrada y luego guarda el resultado en el campo a de la entrada. Luego A avanza, después de lo cual B puede leer esta entrada y a en la entrada. Si el lector C no depende de A, entonces C no debería leer a.
Por supuesto, el principal objetivo de LMAX es el rendimiento. Disruptor utiliza un anillo de entradas preasignadas. El anillo es lo suficientemente grande, pero está tapado para que no se exceda la capacidad. Si el anillo está lleno, los escritores esperan hasta que el escritor más lento avance para hacer espacio. Los objetos de entrada están preasignados y persisten para reducir la sobrecarga de GC. No agregamos nuevos objetos ni eliminamos los antiguos. En cambio, el autor solicita un elemento existente, completa sus campos y notifica a los lectores. Estos dos pasos son operaciones atómicas realmente simples. setNewEntry(EntryPopulator); interface EntryPopulator{ void populate(Entry existenteEntry } Las entradas preasignadas son equivalentes a la asignación de entradas adyacentes en celdas de memoria adyacentes (muy parecidas), y debido a que los lectores leen las entradas secuencialmente, es importante utilizar el Caché de la CPU. También se hacen muchos esfuerzos para evitar bloqueos, CAS e incluso búferes de memoria (como el uso de variables de secuencia inmutables si solo hay un autor). Para desarrolladores: los lectores de diferentes anotaciones deben escribir en diferentes campos de la entrada para evitar conflictos de escritura. (De hecho, deberían escribirse en diferentes líneas de caché.