Análisis del código fuente de Java y preguntas de la entrevista: combate de escena: práctica de ThreadLocal en el contexto de escenarios de paso de valores

Esta serie de blog relacionado, Mu columna de referencia de clase Java de código fuente y del sistema fabricantes entrevistador sucinta Zhenti
por debajo de esta columna es la dirección de GitHub:
Fuente resolvió: https://github.com/luanqiu/java8
artículo Demostración: HTTPS: // GitHub. com / luanqiu / java8_demo los
compañeros de clase pueden verlo si es necesario)

Análisis del código fuente de Java y preguntas de la entrevista: combate de escena: práctica de ThreadLocal en el contexto de escenarios de paso de valores

En los comentarios de apertura,
simplemente hemos mejorado el motor de proceso en el artículo "Impresionando al entrevistador: el uso real de la orquestación de procesos del grupo de subprocesos". Este artículo continúa transformándose en base a ello. Se recomienda que los estudiantes echen un vistazo al código en GitHub, o Mira el artículo anterior.

1 reseña

El objeto orquestado por el motor de proceso se llama componente (es decir, SpringBean). Antes de definir una interfaz común para el componente, la interfaz se implementó cuando se implementó el componente. El código es el siguiente:
Inserte la descripción de la imagen aquí
Definimos la interfaz DomainAbilityBean, y los parámetros de entrada y salida son FlowContent A FlowContent lo llamamos contexto.

2 Implementación ThreadLocal

Además de la implementación de FlowContent, ThreadLocal también se puede lograr. Demostremos:

2.1 Definir clase de herramienta de contexto ThreadLocal

Primero, usamos ThreadLocal para definir la clase de herramienta de contexto, y definimos los métodos put y get para un uso fácil. El código es el siguiente:

public class ContextCache implements Serializable {
 
  private static final long serialVersionUID = 2136539028591849277L;
 
  // 使用 ThreadLocal 缓存上下文信息
  public static final ThreadLocal<Map<String,String>> CACHE = new ThreadLocal<>();
 
  /**
   * 放数据
   * @param sourceKey
   */
  public static final void putAttribute(String sourceKey,String value){
    Map<String,String> cacheMap = CACHE.get();
    if(null == cacheMap){
      cacheMap = new HashMap<>();
    }
    cacheMap.put(sourceKey,value);
    CACHE.set(cacheMap);
  }
 
  /**
   * 拿数据
   * @param sourceKey
   */
  public static final String getAttribute(String sourceKey){
    Map<String,String> cacheMap = CACHE.get();
    if(null == cacheMap){
      return null;
    }
    return cacheMap.get(sourceKey);
  }
 
}

Si desea poner datos en ThreadLocal, llame al método ContextCache.putAttribute, si desea obtener datos de ThreadLocal, llame al método ContextCache.getAttribute.

Escribimos dos componentes, un componente coloca los datos y un componente toma los datos de la siguiente manera:
Inserte la descripción de la imagen aquí
registramos dos SpringBeans en el centro de registro de procesos y los dejamos ejecutar en el orden de ejecutar primero BeanThree y luego BeanFive, y ejecutar la clase principal DemoApplication El método se ejecuta y los resultados son los siguientes:
Inserte la descripción de la imagen aquí
desde el registro impreso, puede ver que en el SpringBean administrado por el contenedor Spring, ThreadLocal también puede almacenar valores de caché intermedios.

3 Iniciar hilo secundario

Hagamos un experimento. Comenzamos el hilo secundario en BeanFive, y luego obtenemos el valor de ThreadLocal para ver si podemos obtener el valor. El código de BeanFive se modifica de la siguiente manera: Ejecútelo
Inserte la descripción de la imagen aquí
nuevamente, y el registro impreso es el siguiente:
Inserte la descripción de la imagen aquí
del registro impreso En, encontramos que cuando no se obtiene el valor de ThreadLocal en el subproceso secundario, la razón es principalmente que dijimos antes, cuando se crea el subproceso, no copiará el valor en ThreadLocal del subproceso principal al subproceso secundario. ThreadLocal, la solución es modificar ThreadLocal a InheritableThreadLocal, el código se modifica de la siguiente manera:
Inserte la descripción de la imagen aquí
ejecutamos nuevamente, los resultados son los siguientes:
Inserte la descripción de la imagen aquí
De los resultados en ejecución, obtenemos con éxito el valor en el subproceso secundario.

Grupo de 4 subprocesos + subproceso local

Si el springBean que tomó los datos fue arrojado al grupo de subprocesos para su ejecución, ¿podemos obtener con éxito los datos de ThreadLocal?

En primer lugar, en el springBean que coloca los datos, modifique el valor para que sea aleatorio y luego modifique el SpringBean que toma los datos para ejecutarse de forma asincrónica. El código se modifica de la siguiente manera:
Inserte la descripción de la imagen aquí
para ver el efecto rápidamente, modificamos todos los coreSize y maxSize del grupo de subprocesos para 3, y deje que la tarea duerma durante un período de tiempo, de modo que los tres subprocesos definitivamente no consuman la tarea, se pondrá en cola una gran cantidad de tareas en la cola, modificamos el script de prueba, de la siguiente manera:
Inserte la descripción de la imagen aquí
esperamos el resultado:

  1. BeanFive ejecutado en el grupo de subprocesos puede obtener datos de ThreadLocal con éxito;
  2. Puede obtener los datos correctos de ThreadLocal, como BeanThree, simplemente ingrese key1, value5, luego espere obtener value5 de acuerdo con key1 en BeanFive en lugar de otros valores.

Ejecútelo y los resultados son los siguientes: a
Inserte la descripción de la imagen aquí
partir de los resultados, podemos ver que no cumplió con nuestras expectativas. Pusimos muchos valores en ThreadLocal, pero muchos de los valores que salieron fueron value379, todos los cuales se pusieron en ThreadLocal. Valor.

La razón principal de esto es que las referencias al HashMap almacenado en ThreadLocal son las mismas. El subproceso principal puede modificar los valores en el HashMap. Cuando el subproceso secundario toma el valor del ThreadLocal, también toma el valor del HashMap, lo que resulta en la imposibilidad de pasar el valor de poner a través del ThreadLocal. Pasado correctamente al hilo secundario.

Para probar esta razón, imprimimos la dirección de memoria de HashMap en el lugar donde colocamos y obtenemos el valor de ThreadLocal, y cambiamos el código de la siguiente manera:
Inserte la descripción de la imagen aquí
ejecutamos el código de prueba nuevamente, y los resultados son los siguientes:
Inserte la descripción de la imagen aquí
de los resultados de la prueba, podemos ver que no importa Cuando el subproceso principal o el subproceso interactúa con ThreadLocal, HashMap es el mismo, lo que significa que el HashMap guardado en ThreadLocal se comparte, lo que conduce al problema de la seguridad del subproceso, y el valor leído por el subproceso se confundirá .

5 soluciones

En respuesta a este problema, hemos propuesto una solución: al enviar la tarea al grupo de subprocesos, hacemos una copia de HashMap, de modo que el HashMap del subproceso secundario y el HashMap del subproceso principal sean diferentes, lo que puede resolver el problema anterior.

Cuando enviamos la tarea, usamos Runnable. Para realizar la copia de HashMap, necesitamos envolver Runnable en una capa. El código de empaquetado es el siguiente: El
Inserte la descripción de la imagen aquí
resultado de ejecución es el siguiente:
Inserte la descripción de la imagen aquí
del resultado de ejecución, podemos ver que el valor del grupo de subprocesos ha sido Es correcto

6 Resumen

Este artículo usa ThreadLocal para transformar la transmisión de contexto en el motor de procesos, con la esperanza de profundizar la comprensión de todos y usar las habilidades de ThreadLocal. Los estudiantes interesados ​​pueden descargar nuestro código y correr.

Publicado 40 artículos originales · ganado elogios 1 · vistas 5355

Supongo que te gusta

Origin blog.csdn.net/aha_jasper/article/details/105609455
Recomendado
Clasificación