Samuel, miembro de este staff, nos hizo el favor de crear esta sección ya que conoce a la perfección dicho tema.
Solución:
Una condición de carrera ocurre cuando dos o más subprocesos pueden acceder a datos compartidos e intentan cambiarlos al mismo tiempo. Debido a que el algoritmo de programación de subprocesos puede cambiar entre subprocesos en cualquier momento, no sabe el orden en que los subprocesos intentarán acceder a los datos compartidos. Por lo tanto, el resultado del cambio en los datos depende del algoritmo de programación de subprocesos, es decir, ambos subprocesos están “compitiendo” para acceder/cambiar los datos.
Los problemas a menudo ocurren cuando un subproceso hace “verificar y luego actuar” (por ejemplo, “verificar” si el valor es X, luego “actuar” para hacer algo que depende de que el valor sea X) y otro subproceso hace algo con el valor en entre el “cheque” y el “acto”. P.ej:
if (x == 5) // The "Check"
y = x * 2; // The "Act"
// If another thread changed x in between "if (x == 5)" and "y = x * 2" above,
// y will not be equal to 10.
El punto es que y podría ser 10, o podría ser cualquier cosa, dependiendo de si otro subproceso cambió x entre la verificación y la acción. No tienes una forma real de saberlo.
Para evitar que ocurran condiciones de carrera, normalmente colocaría un candado alrededor de los datos compartidos para asegurarse de que solo un subproceso pueda acceder a los datos a la vez. Esto significaría algo como esto:
// Obtain lock for x
if (x == 5)
y = x * 2; // Now, nothing can change x until the lock is released.
// Therefore y = 10
// release lock for x
Existe una “condición de carrera” cuando el código de subprocesos múltiples (o de otro modo paralelo) que accedería a un recurso compartido podría hacerlo de tal manera que provoque resultados inesperados.
Toma este ejemplo:
for ( int i = 0; i < 10000000; i++ )
x = x + 1;
Si tuviera 5 subprocesos ejecutando este código a la vez, el valor de x NO terminaría siendo 50,000,000. De hecho, variaría con cada ejecución.
Esto se debe a que, para que cada hilo incremente el valor de x, tienen que hacer lo siguiente: (simplificado, obviamente)
Retrieve the value of x Add 1 to this value Store this value to x
Cualquier subproceso puede estar en cualquier paso de este proceso en cualquier momento, y pueden interponerse entre sí cuando se trata de un recurso compartido. El estado de x puede ser cambiado por otro subproceso durante el tiempo entre que se lee x y cuando se vuelve a escribir.
Digamos que un hilo recupera el valor de x, pero aún no lo ha almacenado. Otro subproceso también puede recuperar el mismo valor de x (porque ningún hilo lo ha cambiado todavía) y ambos estarían almacenando el mismo valor (x+1) de nuevo en x!
Ejemplo:
Thread 1: reads x, value is 7 Thread 1: add 1 to x, value is now 8 Thread 2: reads x, value is 7 Thread 1: stores 8 in x Thread 2: adds 1 to x, value is now 8 Thread 2: stores 8 in x
Las condiciones de carrera se pueden evitar empleando algún tipo de cierre mecanismo antes del código que accede al recurso compartido:
for ( int i = 0; i < 10000000; i++ )
//lock x
x = x + 1;
//unlock x
Aquí, la respuesta sale como 50,000,000 cada vez.
Para obtener más información sobre el bloqueo, busque: mutex, semáforo, sección crítica, recurso compartido.
¿Qué es una condición de carrera?
Estás planeando ir al cine a las 5 pm. Usted pregunta sobre la disponibilidad de los boletos a las 4 pm. El representante dice que están disponibles. Te relajas y llegas a la taquilla 5 minutos antes del espectáculo. Estoy seguro de que puedes adivinar lo que sucede: es una casa llena. El problema aquí estaba en la duración entre el cheque y la acción. Preguntó a las 4 y actuó a las 5. Mientras tanto, alguien más agarró los boletos. Esa es una condición de carrera, específicamente un escenario de "verificar y luego actuar" de las condiciones de carrera.
¿Cómo los detectas?
Revisión de código religioso, pruebas unitarias de subprocesos múltiples. No hay ningún atajo. Hay pocos complementos de Eclipse emergentes en esto, pero nada estable todavía.
¿Cómo los maneja y los previene?
Lo mejor sería crear funciones sin efectos secundarios y sin estado, usar inmutables tanto como sea posible. Pero eso no siempre es posible. Por lo tanto, el uso de java.util.concurrent.atomic, estructuras de datos concurrentes, sincronización adecuada y concurrencia basada en actores ayudará.
El mejor recurso para la concurrencia es JCIP. También puede obtener más detalles sobre la explicación anterior aquí.
Sección de Reseñas y Valoraciones
Si tienes algún atascamiento y forma de progresar nuestro noticia eres capaz de ejecutar un paráfrasis y con gusto lo ojearemos.