Intercambio de tecnología ThreadLocal

Para aprender algo, primero debemos saber por qué lo presentamos y qué podemos hacer con él. Entonces, echemos un vistazo a lo que ThreadLocal hace por nosotros primero y luego echemos un vistazo a su principio de implementación.

Si ThreadLocal parece "hilo local" por su nombre, sólo se puede decir que el nombre no es muy bueno y es fácil de malinterpretar ThreadLocalVariable (variable local del hilo) debería ser un nombre mejor. Echemos un vistazo a la descripción oficial de ThreadLocal:

Esta clase proporciona variables locales de subprocesos. Estas variables son diferentes de sus contrapartes normales porque cada hilo que accede a una variable (a través de su método get o set) tiene su propia variable local, que es independiente de la copia inicial de la variable. Las instancias de ThreadLocal suelen ser campos estáticos privados en la clase y quieren asociar el estado con un determinado hilo (por ejemplo, ID de usuario o ID de transacción).

1. Cada hilo tiene sus propias variables locales

Cada hilo tiene un contexto independiente de otros hilos para guardar esta variable, las variables locales de un hilo son invisibles para otros hilos (hay condiciones, explicadas más adelante)

2. Copia inicializada independiente de variables

ThreadLocal puede dar un valor inicial y cada hilo obtendrá una copia de este valor inicial, para garantizar que los diferentes hilos tengan una copia.

3. El estado está asociado con un determinado hilo.

ThreadLocal no se usa para resolver el problema de las variables compartidas. No existe para coordinar la sincronización de subprocesos, pero se introdujo un mecanismo para facilitar que cada subproceso maneje su propio estado. Comprender esto es esencial para el uso correcto de ThreadLocal

Cuándo usar:

Dé algunos ejemplos para ilustrar:

1. Por ejemplo, existen muchos métodos posibles para procesar un negocio muy complejo en un hilo Entonces, el uso de ThreadLocal puede reemplazar la transmisión explícita de algunos parámetros;

2. Por ejemplo, para almacenar sesiones de usuario. Las características de Session son muy adecuadas para ThreadLocal, porque la Session es válida en el período de sesión actual anterior, y la sesión se destruye cuando finaliza. Analicemos primero el proceso de una solicitud web en general pero de forma incorrecta:

  • El usuario visita la página web en el navegador;
  • El navegador inicia una solicitud al servidor;
  • El controlador de servicios en el servidor (como tomcat) recibe la solicitud e inicia un hilo para procesar la solicitud, durante el cual se utilizará la sesión;
  • Finalmente, el servidor devuelve el resultado de la solicitud al navegador del cliente.

A partir de este simple proceso de acceso, podemos ver que esta sesión se genera y se utiliza en el proceso de procesamiento de una sesión de usuario. Si simplemente entiende que una sesión de un usuario corresponde a un hilo de procesamiento independiente en el lado del servidor, utilice ThreadLocal para almacenar la sesión. No podría ser más adecuado. Sin embargo, el software de servidor como tomcat utiliza tecnología de grupo de subprocesos, que no corresponde a un subproceso en un sentido estricto. No es que esta situación no sea adecuada para ThreadLocal, sino que la sesión anterior debe limpiarse cada vez que entra una solicitud. Generalmente, se pueden usar interceptores y filtros para lograrlo.

3. En algunas situaciones de subprocesos múltiples, si se utiliza la sincronización de subprocesos, el rendimiento se verá afectado cuando la simultaneidad sea relativamente alta y se puede cambiar a ThreadLocal. Por ejemplo, el marco de serialización de alto rendimiento Kyro usará ThreadLocal para garantizar un alto rendimiento y A salvo de amenazas

4. También hay ThreadLocal que se puede utilizar en administradores de subprocesos, conexiones de bases de datos, etc .;

Ahora primero miramos un fragmento de código:

imagen

resultado de la operación:

imagen

Este ejemplo nos dice que las variables entre cada hilo no se afectan entre sí.

Entonces veamos un ejemplo:

imagen

Resultado de salida:

imagen

Eh, ¿por qué cada valor es diferente? ¿No decía que las cosas buenas no se afectan entre sí?

En este momento, tengo que sacar el pincel que no he movido durante tanto tiempo, para explicarte por qué sucede esto.

Echemos un vistazo a la diferencia entre Demo1 y Demo2.

Demo1:

imagen

Demo2:

imagen

Aquí viene el problema: Demo1 devuelve un tipo básico de 0 cada vez. Pero indexnum es un objeto. Entonces, cada vez que apunta al mismo objeto, para profundizar nuestra comprensión, hacemos un dibujo para mostrarlo.

imagen

Entonces ThreadLocal solo guarda una copia de la dirección del objeto, y todos los objetos que inicializamos apuntan a la misma dirección, por lo que habrá tal problema. Entonces, ¿cómo resolvemos este problema? en realidad es muy fácil

imagen

Solo necesita un objeto nuevo cada vez que sea nuevo, para que no apunte a la misma dirección.

Veamos el resultado de salida:

imagen

A continuación, echemos un vistazo al código fuente interno, Xiao Dai, y lo llevamos a leer el código fuente de ThreadLocal.

imagen

Hay varios métodos en ThreadLocal. El principal es

public T get() { }

public  void set(T value) { }

public  void remove() { }

protected T initialValue() { }

El método get () se usa para obtener una copia de la variable guardada por ThreadLocal en el hilo actual, set () se usa para establecer una copia de la variable en el hilo actual, y remove () se usa para eliminar la copia de la variable en el hilo actual. InitialValue () es un El método protegido se usa generalmente para reescribir durante el uso, es un método de carga diferida

1: obtener método:

imagen

  1. Obtenga el hilo actual primero
  2. Determinar si el hilo actual contiene ThreadLocalMap
  3. Si el mapa es nulo o la variable local en el mapa está vacía, cree el valor inicial

imagen

Al crear el valor inicial, determinará si el mapa existe,

  1. Si no existe, inicialice el mapa y establezca la clave y el valor del mapa
  2. Si existe, establezca directamente la clave y el valor del mapa

imagen

Esta es la operación de obtención de ThreadLocal.

2: método de configuración:

imagen

También es muy similar obtener el hilo actual primero, y luego del ThreadLocalMap del hilo actual si el valor establecido existe, entonces el conjunto no existe, luego inicializar y establecer el valor.

3: Eliminar método:

imagen

Obtener el hilo actual, la instancia del hilo actual es la clave del mapa para obtener y eliminar.

Estos son los tres métodos de ThreadLocal más utilizados.

A continuación, aprendamos sobre ThreadLocalMap en ThreadLocal

imagen

A través del análisis anterior, sabemos que cuando se usa ThreadLocal para guardar un valor, se insertará un objeto Entry en la matriz en ThreadLocalMap. Es lógico que el valor-clave se almacene en el objeto Entry con una referencia fuerte, pero en la implementación de ThreadLocalMap, la clave Se guarda en el objeto WeakReference.

Esto conduce a un problema. ThreadLocal se reciclará cuando se produzca GC cuando no haya una referencia externa fuerte. Si el hilo que creó ThreadLocal continúa ejecutándose, es posible que el valor del objeto Entry no se recicle y que se produzcan pérdidas de memoria. .

imagen

Entonces, algunas personas pueden preguntar por qué se utilizan referencias débiles. De hecho, esto tiene algo que ver con la filosofía de diseño de Java. Java siempre ha abogado por debilitar la gestión de punteros. Entonces, la clave es cómo cotizar. Comparemos las dos situaciones

  1. La clave usa una referencia fuerte: el objeto ThreadLocal al que se hace referencia se recicla, pero ThreadLocalMap aún tiene una fuerte referencia al ThreadLocal. Si no se elimina manualmente, el ThreadLocal no se reciclará, lo que resultará en una pérdida de memoria de entrada.
  2. La clave usa una referencia débil: el objeto ThreadLocal al que se hace referencia se recicla. Debido a que ThreadLocalMap contiene una referencia débil a ThreadLocal, incluso si no se elimina manualmente, ThreadLocal se reciclará. El valor se borrará la próxima vez que ThreadLocalMap llame a set, get y remove.

Comparando los dos casos, podemos encontrar que: Dado que el ciclo de vida de ThreadLocalMap es tan largo como Thread, si la clave correspondiente no se elimina manualmente, causará pérdidas de memoria, pero el uso de referencias débiles puede proporcionar una capa adicional de protección: las referencias débiles a ThreadLocal no perderán memoria. El valor correspondiente se borrará la próxima vez que ThreadLocalMap llame a set, get y remove.

Por lo tanto, la causa raíz de la pérdida de memoria de ThreadLocal es: dado que el ciclo de vida de ThreadLocalMap es tan largo como el Thread, si la clave correspondiente no se elimina manualmente, causará una pérdida de memoria, no debido a referencias débiles.

En resumen

Cuando usamos ThredLocal nuevamente, debemos llamar al método remove después de usarlo. borrar datos.

¡Lecciones de sangre y lágrimas! ! !

accidente p3

Aún recuerdo ese día 26 de marzo, no lo olvidaré. Después de despertarme al mediodía de ese día, mi amigo me llevó a suerte en el café para comprar una botella de café, y me sentí feliz. Es otro registro con café y código de escritura. En este momento, el grupo técnico interno informó de repente que la información de inicio de sesión del comerciante se serializó. A través, mi cerebro está zumbando ~ En este momento, estaba pensando por qué no cambiamos el código por qué ocurrió tal problema.

En ese momento, me di cuenta de la gravedad del problema y regresé rápidamente a la oficina. En ese momento, el tiempo de avería había superado los 5 minutos. Abrí la interfaz ci y retrocedí inmediatamente. Afortunadamente, el comerciante fue liberado rápidamente y la reversión se completó en 5 minutos. Se notificó al grupo de operaciones que permitiera al comerciante actualizar la página y se restauró la falla.

De hecho, cuando retrocedí, me di cuenta de cuál era el problema (no se resolvió después de que se usó ThreadLocal), y lo que inmediatamente me vino a la mente fue por qué no había ningún problema antes y ahora lo causaba. Más tarde, la actualización a la versión del paquete de primavera llevó al orden de ejecución de aop:

imagen

Hemos creado una solución de abajo hacia arriba en nuestro código, que es eliminar la información ThreadLocal utilizada actualmente a través de aop. Pero después de que se actualizó la versión del paquete de primavera, aop primero implementó esta solución y luego ejecutó otros escenarios que usaban ThreadLocal.

Una vez que se borra el resultado, la operación Get se ejecuta nuevamente, lo que hace que otros usuarios accedan y obtengan información del usuario en otros grupos de subprocesos (reutilización del grupo de subprocesos de tomcat)

En respuesta a esta situación, se realizó una corrección al resultado final. Utilice el filtro para lograr el esquema de fondo porque la orden de ejecución es

antes de:

Aop no especifica el orden, por lo que no hay forma de tratarlo.

después:

Configure un filtro en el exterior, configure y elimine ThreadLocal en el interior

imagen

imagen

Configure el filtro en la capa más externa. El ajuste ThreadLocal solo se coloca en el Filtro. Después de que se ejecuta el método, ThreadLocal se borra en el filtro.

ps: Independientemente de si existe esta capa de bolsillos o no, después de usar ThreadLocal, revome se coloca en el bloque de código final.

 

Supongo que te gusta

Origin blog.csdn.net/suifeng629/article/details/107119773
Recomendado
Clasificación