Cómo implementar la concurrencia multiproceso de springMVC
Utilizamos clases de plantilla para acceder a los datos subyacentes. Según la tecnología de persistencia, las clases de plantilla necesitan vincular recursos para conexiones o sesiones de datos. Sin embargo, estos recursos no son inherentemente seguros para subprocesos, lo que significa que varios subprocesos no pueden acceder a ellos al mismo tiempo.
Aunque la clase de plantilla obtiene la conexión o sesión de datos a través del grupo de recursos, el grupo de recursos en sí resuelve el problema de almacenar en caché la conexión o sesión de datos, no el problema de seguridad de los subprocesos de la conexión o sesión de datos.
Según la experiencia tradicional, si un objeto no es seguro para subprocesos, en un entorno de subprocesos múltiples, el acceso al objeto debe utilizar la sincronización de subprocesos. Sin embargo, las clases de plantilla DAO de Spring no utilizan la sincronización de subprocesos porque la sincronización de subprocesos limita el acceso concurrente, lo que puede provocar graves pérdidas de rendimiento.
Además, resolver problemas de seguridad del rendimiento mediante la sincronización de código es un gran desafío y su implementación puede ser varias veces más difícil. Entonces, ¿qué tienen de mágico las clases de plantilla que resuelven problemas de seguridad de subprocesos sin sincronización?
ThreadLocal juega un papel importante en Spring. Aparecen en módulos como la gestión de beans de alcance de solicitudes, la gestión de transacciones, la programación de tareas y AOP, y desempeñan un papel decisivo. Para comprender la tecnología subyacente de la gestión de transacciones de Spring, ThreadLocal es una fortaleza montañosa que debe ser conquistada.
Qué es ThreadLocal
java.lang.ThreadLocal se proporcionó ya en JDK1.2. ThreadLocal proporciona una nueva idea para resolver problemas de concurrencia de programas multiproceso. Con esta clase de herramienta, puede escribir hermosos programas multiproceso de manera muy concisa.
ThreadLocal Es fácil asumir que es un "hilo local". De hecho, ThreadLocal no es un hilo, sino una variable local del hilo. Puede ser más fácil de entender si se llama ThreadLocalVariable.
Cuando se usa ThreadLocal para mantener una variable, ThreadLocal proporcionará una copia independiente para cada hilo que usa la variable, por lo que cada hilo puede cambiar de forma independiente su propia copia sin afectar las copias correspondientes de otros hilos.
Desde la perspectiva del hilo, la variable de destino es como una variable local del hilo, que es lo que significa "Local" en el nombre de la clase.
Las variables locales de subproceso no son una característica nueva de Java; muchos lenguajes (como IBM IBM XLFORTRAN) proporcionan variables locales de subproceso a nivel de sintaxis. En Java, no brindamos soporte a nivel de lenguaje, sino que disfrazamos este soporte a través de la clase ThreadLocal.
Por lo tanto, escribir código para variables locales de subprocesos en Java es relativamente difícil de manejar, por lo que las variables locales de subprocesos no se utilizan ampliamente entre los desarrolladores de Java.
Métodos de la interfaz ThreadLocal
La interfaz de la clase ThreadLocal es muy simple, con solo cuatro métodos:
void set(Valor del objeto).
Establece el valor de la variable local del hilo del hilo actual.
Objeto público get()
Este método devuelve la variable local del hilo correspondiente al hilo actual.
public void remove()
Elimina el valor de la variable local del hilo actual para reducir el uso de memoria. Este método es un método nuevo en JDK 5.0. Cabe señalar que cuando finaliza un subproceso, las variables locales correspondientes al subproceso se recolectarán automáticamente como basura, por lo que no es necesario llamar explícitamente a este método para borrar las variables locales del subproceso, pero puede acelerar la recuperación de la memoria.
Objeto protegido inicialValue()
Devuelve el valor inicial de una variable local de subproceso. Este es un método protegido, obviamente diseñado para sobrecarga por subclases. Este método es un método diferido que se ejecuta solo la primera vez que un hilo llama a get() o set(Object), y solo una vez.
Vale la pena señalar que en JDK 5.0, ThreadLocal se generalizó y el nombre de clase de esta clase se cambió a ThreadLocal
¿Cómo mantiene ThreadLocal una copia de las variables para cada hilo? La idea de implementación es realmente muy simple: hay un Map en la clase ThreadLocal para almacenar una copia de las variables de cada hilo, donde la clave del elemento Map es el objeto del hilo y el valor corresponde al copia del hilo de la variable. Podemos proporcionar una versión simple de esta implementación nosotros mismos:
/ Listado de código 1 SimpleThreadLocal
class SimpleThreadLocal {
private MapvalueMap = Collections.synchronizedMap(new HashMap( ));
public voidset(Object newValue) {
valueMap.put(Thread.currentThread(), newValue);//① la clave es el objeto del hilo. get( currentThread);// ② Devuelve la variable correspondiente a este hilo
if (o == null &&!valueMap.containsKey(currentThread)) {// ③ Si no existe en el Mapa, luego colóquelo en el mapa
// y guárdelo.
o = valorinicial();
valueMap.put(currentThread, o);
}
return o;
}
public voidremove() {
valueMap.remove(Thread.currentThread());
}
publicObject inicialValue() {
return null;
}
}
Aunque esta versión de la implementación ThreadLocal en el Listado 9.3 parece ingenuo, pero su idea de implementación es similar a la clase ThreadLocal proporcionada por el JDK.
Ejemplo de TheadLocal
El siguiente es un ejemplo específico de cómo usar ThreadLocal
paquete threadLocalDemo;
clase pública SequenceNumber { p>
//① Anula el método inicialValue() de ThreadLocal a través de una clase interna anónima para especificar el valor inicial
privatestatic ThreadLocal<Integer> seqNum =new ThreadLocal público entero valor inicial() { retorno 0; } }; / /②Obtener el siguiente valor de secuencia public intgetNextNum() { seqNum.set(seqNum.get() + 1); Devuelve seqNum. get(); } publicstatic void main(String[] args) { SequenceNumber sn = new SequenceNumber() ; // ③ 3 hilos**** disfrutan de sn, cada hilo genera un número de secuencia TestClient t1 = new TestClient(sn); TestClient t2 = nuevo TestClient(sn); TestClient t3 = nuevo TestClient(sn); t1.start(); t2.start( ); t3.start(); } clase privada estática TestClient extiende Thread { número de secuencia privado sn; public TestClient(SequenceNumber sn) { this.sn = sn; } public void run() { for (int i = 0; i < 3; i++) { // ④ Cada hilo alcanza 3 valores de secuencia p> System.out.println(" thread[" + Thread.currentThread().getName() + "]sn[" + sn.getNextNum() + "]"); } } } } Por lo general, lo definimos a través de una subclase interna anónima de ThreadLocal que proporciona el valor de la variable inicial, como se muestra en ejemplo ①. El hilo TestClient genera un conjunto de números de secuencia. En ③, generamos 3 TestClients, que comparten la misma instancia de SequenceNumber. Ejecute el código anterior y genere el siguiente contenido en la consola: thread[Thread-2] sn[1] thread[Thread-0] sn[1] hilo[Thread-1] sn[1] hilo[Thread-2] sn[ 2] hilo[Thread-0] sn[2] p> hilo[Hilo-1] sn[2] hilo[Hilo-2] sn[3] hilo[Hilo-0] sn[3] thread[ Thread-1] sn[3] Al verificar la información de salida, encontramos que aunque los números de secuencia generados por cada hilo comparten la misma instancia de SequenceNumber, no hay no hay interferencia mutua. En cambio, cada hilo genera un número de secuencia independiente. Esto se debe a que proporcionamos un ThreadLocal para cada hilo y cada hilo tiene una copia separada. Comparación de mecanismos de sincronización de subprocesos En comparación con los mecanismos de sincronización de subprocesos, ¿cuáles son las ventajas de ThreadLocal? Tanto ThreadLocal como los mecanismos de sincronización de subprocesos están diseñados para resolver el problema de los conflictos cuando varios subprocesos acceden a las mismas variables. En el mecanismo de sincronización, el mecanismo de bloqueo del objeto garantiza que solo un hilo pueda acceder a la variable a la vez. En este momento, las variables son disfrutadas por múltiples subprocesos. El uso del mecanismo de sincronización requiere que el programa analice cuidadosamente cuestiones complejas como cuándo leer y escribir variables, cuándo bloquear un objeto y cuándo liberar el bloqueo del objeto. son relativamente más difíciles. ThreadLocal resuelve el problema del acceso simultáneo a múltiples subprocesos desde otro ángulo: ThreadLocal proporciona una copia separada de variables para cada subproceso, aislando así los conflictos de acceso a datos entre múltiples subprocesos. Dado que cada hilo tiene su propia copia de la variable, no es necesario sincronizar la variable. ThreadLocal proporciona objetos seguros para subprocesos, por lo que al escribir código multiproceso, puede encapsular variables inseguras en ThreadLocal. Porque cualquier tipo de objeto se puede guardar en ThreadLocal. get() proporcionado por versiones anteriores de JDK devolverá objetos Object, y los objetos Object requieren conversión de tipo de conversión. Sin embargo, JDK5.0 ha resuelto bien este problema mediante la generalización, simplificando el uso de ThreadLocal hasta cierto punto. El Listado de Código 9 2 presenta el uso de la nueva versión ThreadLocal En resumen, el mecanismo de sincronización utiliza el método "tiempo por espacio" para disfrutar de recursos multiproceso, mientras que ThreadLocal utiliza el método "espacio por tiempo". El primero solo proporciona una copia de la variable para que diferentes subprocesos pongan en cola para acceder, mientras que el segundo proporciona a cada subproceso una copia de la variable para que se pueda acceder a ella simultáneamente sin afectarse entre sí. Spring usa ThreadLocal para resolver problemas de seguridad de subprocesos Sabemos que, en general, solo se pueden usar beans sin estado en un entorno de subprocesos múltiples y, en Spring, la mayoría de los beans se pueden declarar. con alcance singleton. Esto se debe a que Spring usa ThreadLocal para el estado no seguro para subprocesos de ciertos beans (como RequestContextHolder, TransactionSynchronizationManager, LocaleContextHolder, etc.) para hacerlos seguros para subprocesos, ya que los beans con estado se pueden declarar con alcance singleton. Estado seguro porque los beans con estado pueden estar en varios subprocesos. Las aplicaciones Web generales se dividen en tres capas: capa de presentación, capa de servicio y capa de persistencia. La lógica correspondiente está escrita en diferentes capas y la capa inferior abre llamadas de función a la capa superior a través de interfaces. En términos generales, todas las llamadas al programa, desde la recepción de una solicitud hasta la devolución de una respuesta, pertenecen al mismo hilo, como se muestra en la Figura 9?2: Figura 1 Tres capas pasadas por el mismo hilo Por lo tanto, puede convertir algunas variables que no son seguras para subprocesos al almacenamiento ThreadLocal si es necesario. En el mismo subproceso de llamada de solicitud-respuesta, todas las referencias de los objetos asociados apuntan al mismo almacenamiento. todas las referencias de los objetos asociados apuntan a la misma variable. El siguiente ejemplo ilustra el manejo de Spring de beans con estado: Listado de código 3 TopicDao: no seguro para subprocesos clase pública TopicDao { Conexión privada;①Variables no seguras para subprocesos< public void addTopic(){ Declaración stat = conn.createStatement(); ②Referencia a variables no seguras para subprocesos; .. } } Dado que ① en conn es una variable miembro y el método addTopic() no es un hilo -safe , por lo que se debe crear una nueva instancia de TopicDao (no una "funda") al utilizar este método. A continuación se explica cómo convertir el "estado" de conexión no seguro para subprocesos utilizando ThreadLocal: Listado de código 4 TopicDao: Declaración; clase pública SqlConnection { //① Utilice ThreadLocal para guardar variables de conexión privatestatic ThreadLocal publicstatic Connection getConnection() { // ② Si connThreadLocal no tiene una Conexión correspondiente a este hilo, cree una nueva Conexión // y guárdela en la variable local del hilo. if (connThreadLocal.get() == null) { Conexión conn = getConnection(); connThreadLocal.set(conn); return conn; } else { return connThreadLocal.get(); // ③ Devuelve directamente variables locales del hilo } } } public voidaddTopic() { // ④ Obtener variables locales de hilo de ThreadLocal para obtener la conexión correspondiente al hilo try { Statement stat = getConnection().createStatement(); } catch (SQLException e) { e.printStackTrace(); } } } }