¿Cómo garantizar que las colecciones sean seguras para subprocesos?
La simultaneidad es una palabra familiar. En pocas palabras, significa que la CPU ejecuta múltiples tareas al mismo tiempo.
La concurrencia de Java se implementa mediante subprocesos múltiples.
En el mundo de JVM, los subprocesos son como espacios paralelos no relacionados, serializados en la máquina virtual. (Por supuesto, esta es una afirmación más general. Los subprocesos pueden interactuar entre sí y no necesariamente son en serie).
La existencia de subprocesos múltiples sirve para exprimir la CPU, mejorar el rendimiento del programa y Reducir una cierta cantidad de tiempo en la complejidad del diseño (diseñar programas con pensamiento de tiempo realista).
Parece que los hilos son la legendaria bala de plata, pero los hechos nos dicen que la verdadera bala de plata no existe.
Múltiples subprocesos provocarán muchos problemas inevitables, como interbloqueos, datos sucios, sobrecarga adicional de gestión de subprocesos, etc. Esto aumenta enormemente la complejidad de la programación.
Pero sus ventajas siguen siendo irremplazables.
Los puntos muertos y los datos sucios son problemas típicos de seguridad de subprocesos.
En pocas palabras, la seguridad de subprocesos significa: en un entorno de subprocesos múltiples, siempre se puede garantizar la corrección del programa.
Los problemas de seguridad de los subprocesos deben considerarse solo cuando hay datos compartidos.
Área de memoria de Java:
Entre ellos, el área de método y el montón son las principales áreas compartidas por subprocesos. Eso significa que el objeto compartido sólo puede ser el dominio de atributos o el dominio estático de la clase.
Después de comprender algunos conceptos básicos sobre problemas de seguridad de subprocesos, hablemos sobre cómo resolver los problemas de seguridad de subprocesos. Analicemos un ejemplo de servlet simple: la clase pública ReqCounterServlet extiende HttpServlet {private int count = 0;
public void doGet(solicitud HttpServletRequest,
respuesta HttpServletResponse) lanza IOException, ServletException { p>
count;
System.out.print("El número actual de solicitudes alcanzadas es " count);
}
public void doPost (Solicitud HttpServletRequest,
respuesta HttpServletResponse) arroja IOException, ServletException { // ignorar }
}1. Comprender el modelo de subprocesos de escenarios comerciales
El modelo de subprocesos. Aquí se refiere a las llamadas de subprocesos reales que pueden ocurrir en este escenario comercial.
Como todos sabemos, el servlet está diseñado como una instancia única. Después de que la solicitud ingresa a Tomcat, el conector establece la conexión y luego la solicitud se distribuye al procesador en el grupo de subprocesos interno. >
En este momento, el Servlet solo está en un entorno de subprocesos múltiples. Es decir, si hay varias solicitudes para acceder a un servlet al mismo tiempo, puede haber varios subprocesos accediendo al objeto servlet al mismo tiempo. Como se muestra en la imagen:
Si el modelo del hilo es simple, simplemente simúlelo en su mente. Si es complejo, puede dibujarlo con lápiz y papel u otras herramientas.
2. Encuentre el objeto compartido
El objeto compartido aquí es obviamente ReqCounterServlet.
3. Analizar las condiciones de inmutabilidad de los objetos compartidos.
Condiciones de invariancia, este término está en el concepto de programación de contratos.
Las condiciones de inmutabilidad garantizan que el estado de la clase permanezca en un estado aceptable después de ejecutar cualquier función.
De aquí se puede deducir que los objetos inmutables son seguros para subprocesos. (Porque los objetos inmutables no tienen condiciones de inmutabilidad)
Las condiciones de inmutabilidad consisten principalmente en modificaciones y accesos a estados mutables.
El servlet aquí es muy simple y las condiciones de invariancia se pueden resumir aproximadamente de la siguiente manera: el recuento debe incrementarse en uno cada vez que llega una solicitud y el recuento debe ser correcto.
En negocios complejos, a menudo es difícil considerar completamente las condiciones de invariancia de las clases. El mundo del diseño es peligroso, por lo que sólo puedes ser cauteloso, utilizar medidas para demostrarlo y minimizar la posibilidad de errores.
4. Utilice estrategias específicas para resolver problemas de seguridad de subprocesos.
Cómo resolverlo es, de hecho, el foco de este proceso. Actualmente existen tres formas de solucionarlo:
La primera es modificar el modelo de hilo. Es decir, la variable de estado no se comparte entre subprocesos. Generalmente, este cambio es relativamente grande y debe realizarse dentro de las posibilidades de cada uno.
El segundo método consiste en convertir el objeto en un objeto inmutable. A veces no sucede.
El tercer tipo es más general y utiliza la sincronización al acceder a variables de estado. Tanto sincronizado como Lock pueden lograr la sincronización. En pocas palabras, cuando modifica o accede al estado mutable, lo bloquea y monopoliza el objeto para que otros subprocesos no puedan ingresar.
Esto también puede considerarse como un método de aislamiento de subprocesos. (Este método también tiene muchas deficiencias, como interbloqueos, problemas de rendimiento, etc.)
De hecho, existe una forma mejor, que consiste en diseñar clases seguras para subprocesos. "Code Encyclopedia" mencionó que cuanto antes se resuelva el problema, menos costoso será.
Sí, es mucho más fácil considerar los problemas de seguridad de los subprocesos al diseñar.
Primero considere si esta clase existirá en un entorno multiproceso. De lo contrario, no se considera la seguridad del hilo.
Luego considere si la clase se puede diseñar como un objeto inmutable o si es un objeto inmutable. Si es así, no considere la seguridad de subprocesos
Finalmente, diseñe clases seguras para subprocesos de acuerdo con el proceso.
Diseñar un proceso seguro para subprocesos:
1. Encuentre todas las variables que constituyen el estado del objeto.
2. Encuentre las condiciones de invariancia que restringen las variables de estado.
3. Establecer una estrategia de gestión de acceso concurrente para el estado de los objetos.
Hay dos estrategias de gestión de acceso concurrente comúnmente utilizadas:
1. Utilice siempre un candado en un objeto para proteger un determinado estado.
2. Delegación segura para subprocesos. Delegue la seguridad de subprocesos de la clase a una o más variables de estado seguras para subprocesos. (Tenga en cuenta que cuando hay varias, estas variables deben ser independientes entre sí y no hay condiciones de invariancia asociadas).