Todavía hay una diferencia entre ThreadLocal y las variables de miembro del hilo. La clase ThreadLocal proporciona variables locales de hilo. Esta variable local es diferente de las variables de miembros generales. Cuando la variable de hilo de ThreadLocal es utilizada por múltiples hilos, cada hilo solo puede obtener una copia de la variable. Esta es una descripción en la API de Java. Al leer el código fuente de API, descubrí que no es una copia. ¿Cuál es el concepto de una copia? Clones? O algo más, demasiado vago.
Para ser precisos, el registro (MAP <Thread, T>) dentro de la variable de tipo ThreadLocal ha cambiado, pero la variable de tipo ThreadLocal en sí es de hecho una, ¡y esta es la esencia!
Aquí hay un ejemplo:
1. Ejemplos estándar
La clase MyThreadLocal se define, y su objeto TLT se crea, y es utilizado por cuatro hilos. Como resultado, las variables TLT de los cuatro hilos no comparten. El segundo es usar el suyo, que muestra que los cuatro hilos usan una copia de TLT (clon).
/*** Use la clase ThreadLocal*/public class MyThreadLocal {// Definir una variable ThreadLocal para guardar int o Integer Data private ThreadLocal <integer> tl = new ThreadLocal <Integer> () {@Override Integer InitialValue () {return 0; }}; Public Integer getNextNum () {// Obtenga el valor de TL y agregue 1, y actualice el valor de t1 tl.set (tl.get () + 1); return tl.get (); }} / *** Test Thread*/ Public Class TestThread extiende el hilo {private myThreadLocal tlt = new MyThreadLocal (); public testThread (myThreadLocal tlt) {this.tlt = tlt; } @Override public void run () {for (int i = 0; i <3; i ++) {system.out.println (thread.currentThread (). GetName () + "/t" + tlt.getNextNum ()); }}} / *** testlocal test*/ public class test {public static void main (string [] args) {myThreadLocal tlt = new MyThreadLocal (); Hilo t1 = nuevo TestThread (TLT); Hilo t2 = nuevo TestThread (TLT); Hilo t3 = nuevo TestThread (TLT); Hilo t4 = nuevo TestThread (TLT); t1.start (); t2.start (); t3.start (); t4.Start (); }}
Se puede ver que los tres hilos están numerados de forma independiente y no se afectan entre sí:
Hilo-0 1 hilo-1 1 hilo-0 2 hilo-1 2 hilo-0 3 hilo-1 3 hilo-2 1 hilo-3 1 hilo-2 2 hilo-3 2 hilo-2 3 hilo-3 3 proceso terminado con código de salida 0
El objeto TLT es uno, y el objeto TL sin sentido también es uno, porque la relación combinada es uno a uno. Sin embargo, a medida que aumenta el número de hilos, se crearán muchos objetos enteros. Es solo que el entero e int ya son comunes. Así que no puedo sentir las propiedades del objeto del entero.
2. No use ThreadLocal
Si no usa ThreadLocal, solo necesita redefinir la clase MyThreadLocal como:
/ *** Use la clase ThreadLocal*/ public class MyThreadLocal {private Integer t1 = 0; Public Integer getNextNum () {return t1 = t1+1; } // Definir una variable ThreadLocal para guardar datos int o enteros // private ThreadLocal <Integer> tl = new ThreadLocal <Integer> () {// @Override // Integer protegido InitialValue () {// return 0; //} //}; // // public Integer getNextNum () {// // Obtener el valor de TL y agregar 1, y actualizar el valor de t1 // tl.set (tl.get () + 1); // return tl.get (); //}}
Luego ejecute la prueba:
Hilo-2 1 hilo-2 2 hilo-1 4 hilo-1 6 hilo-3 3 hilo-3 9 hilo-3 10 hilo-1 8 hilo-0 7 hilo-0 11 hilo-0 12 hilo-2 5 proceso terminado con código de salida 0
Desde aquí, podemos ver que los cuatro hilos comparten la variable TLT, y cada hilo modifica directamente las propiedades del TLT.
3. Date cuenta de ThreadLocal solo
paquete com.avasoft.test2; import java.util.collections; import java.util.hashmap; import java.util.map; /*** Use la clase ThreadLocal*/public class MyThreadLocal {// Definir una variable ThreadLocal para guardar int o Integer Data privado com.lavasoft.test2.threadlocal <integer> tl = new com.avasoft.test2.threadlocal <integer> () { @@Override protegido InitialValue () {Return 0; }}; Public Integer getNextNum () {// Obtenga el valor de TL y agregue 1, y actualice el valor de t1 tl.set (tl.get () + 1); return tl.get (); }} class ThreadLocal <T> {private Map <Thread, t> map = colección.synchronizedMap (new HashMap <Thread, t> ()); public ThreadLocal () {} protegido t inicialValue () {return null; } public t get () {Thread t = Thread.CurrentThread (); T obj = map.get (t); if (obj == null &&! map.containskey (t)) {obj = inicialValue (); map.put (t, obj); } return obj; } Public void set (t value) {map.put (thread.currentThread (), valor); } public void remove () {map.remove (thread.currentThread ()); }}
Ejecute la prueba:
Hilo-0 1 hilo-0 2 hilo-0 3 hilo-2 1 hilo-2 2 hilo-3 1 hilo-2 3 hilo-3 2 hilo-1 1 hilo-3 3 hilo-1 2 hilo-1 3 proceso terminado con código de salida 0
Sorprendentemente, esta versión imitadora de ThreadLocal también funciona bien, implementando la función de ThreadLocal en la API Java.
4. Ver la esencia a través de los fenómenos
De hecho, desde una perspectiva del programa, la variable TLT es de hecho una, sin duda. Pero, ¿por qué los números impresos no se afectan entre sí?
¿Se debe a usar entero? -----No.
La razón es: protegido t inicialValue () y get (), porque cuando cada hilo llame get (), lo creará si no existe en el mapa. Cuando se llama, se crea una nueva variable con el tipo T. Cada vez que se crean recientemente, por supuesto, cada uno los usa sin ningún efecto entre sí.
Para ver claramente la esencia, reemplace el entero y reescribe algunas clases:
paquete com.avasoft.test2; import java.util.collections; import java.util.hashmap; import java.util.map; /*** Use la clase ThreadLocal*/public class myThreadLocal {// Definir una variable ThreadLocal para guardar datos int o enteros // private ThreadLocal <Bean> tl = new ThreadLocal <Bean> () {private com.Lavasoft.test2.ThThreadLocal <Bean> tl = new Com.Lavasoft.test2.Threadlocal <Sta> () inicialValue () {return new Bean (); }}; @Override public String toString () {return "myThreadLocal {" + "tl =" + tl + '}'; } public bean getBean () {return tl.get (); }} class ThreadLocal <T> {private Map <Thread, t> map = colección.synchronizedMap (new HashMap <Thread, t> ()); public ThreadLocal () {} protegido t inicialValue () {return null; } public t get () {Thread t = Thread.CurrentThread (); T obj = map.get (t); if (obj == null &&! map.containskey (t)) {obj = inicialValue (); map.put (t, obj); } return obj; } Public void set (t value) {map.put (thread.currentThread (), valor); } public void remove () {map.remove (thread.currentThread ()); }} paquete com.avasoft.test2; / ** * Test Bean */ public class Bean {private String id = "0"; Nombre de cadena privada = "Ninguno"; public bean () {} public bean (ID de cadena, nombre de cadena) {this.id = id; this.name = name; } public String getId () {return id; } public void setid (ID de cadena) {this.id = id; } public String getName () {nombre de retorno; } public void setName (nombre de cadena) {this.name = name; } public String showInfo () {return "bean {" + "id = '" + id +'/'' + ", name = '" + name +'/'' + '}'; }} paquete com.avasoft.test2; / *** Test Thread*/ Public Class TestThread extiende el hilo {private myThreadLocal tlt = new MyThreadLocal (); public testThread (myThreadLocal tlt) {this.tlt = tlt; } @Override public void run () {system.out.println (">>>>:" + tlt); for (int i = 0; i <3; i ++) {system.out.println (thread.currentThread (). getName ()+"/t"+tlt.getBean ()+"/t"+tlt.getBean (). showInfo ()); }}}
Luego ejecute la prueba:
>>>>>: myThreadLocal (tl=com.lavasoft.test2.mythreadlocal$1@1de3f2d} >>>>>>: mythreadlocal (tl=com.lavasoft.test2.mythreadlocal$1@1de3f2d} >>>>>: mythreadlocal (tl=com.lavasoft.test2.bean@291aff bean {id = '0', name = 'none'} thread-2 com.lavasoft.test2.bean@fe64b9 bean {id = '0', name = 'none'} hilo-3 com.lavasoft.test2.bean@186db54 beay name = 'None'} Thread-2 com.lavasoft.test2.bean@fe64b9 bean {id = '0', name = 'none'} thread-2 com.lavasoft.test2.bean@fe64b9 bean {id = '0', name = 'none'} hilo-2 com.lavasoft.test2.bean@fe64b9 bean {id = '' 0 ', name-2}}}} }-0', ninter. com.lavasoft.test2.bean@291aff bean {id = '0', name = 'none'} thread-3 com.lavasoft.test2.bean@186db54 bean {id = '0', name = 'none'} hilo-3 com.lavasoft.test2.bean@186db54 bean com.lavasoft.test2.bean@291aff bean {id = '0', name = 'none'} thread-0 com.lavasoft.test2.bean@291aff bean {id = '0', name = 'none'} hilo-0 com.lavasoft.test2.bean@291faff bean {id = '0', name = 'none'} hilo-1 com.lavasoft.test2.bean@291 bean {id = '0', name = 'none'} hilo-0 com.lavasoft.test2.bean@291aff bean {id = '0', name = 'none'} Proceso terminado con el código de salida 0
De los resultados de impresión está claro que el objeto TLT de MyThreadLocal es de hecho uno, y el objeto TL de ThreadLocal en el objeto TLT también es uno. Sin embargo, cuando se usa T1T para cada subproceso, el hilo recreará el objeto de frijoles y lo agregará al mapa de ThreadLocal para su uso.
Varios malentendidos sobre ThreadLocal:
1. Threadlocal es una implementación de Java Threads
Threadlocal está relacionado con los hilos Java, pero no es una implementación de hilos Java, solo se usa para mantener variables locales. Para cada hilo, proporciona su propia versión variable, principalmente para evitar conflictos de subprocesos, y cada hilo mantiene su propia versión. Ser independientes el uno del otro, y la modificación no se afectará entre sí.
2. Threadlocal es relativo a cada sesión
Threadlocal, como su nombre lo indica, está dirigido a hilos. En la programación web de Java, cada usuario tiene su propio identificador de sesión desde el principio hasta el final de la sesión. Pero ThreadLocal no está en la capa de sesión. De hecho, ThreadLocal es independiente de la sesión de usuario. Es un comportamiento del lado del servidor. Cada vez que un servidor genera un nuevo hilo, mantiene su propio ThreadLocal.
Con respecto a este malentendido, personalmente creo que debería ser el resultado de la prueba local del desarrollador basada en algunos servidores de aplicaciones. Como todos sabemos, los servidores de aplicaciones generales mantienen un conjunto de grupos de subprocesos, es decir, para cada acceso, un nuevo hilo no necesariamente genera. En cambio, tengo una piscina de caché de hilo. Para el acceso, primero encuentre los hilos existentes desde el grupo de caché. Si han usado todo, se generará un nuevo hilo.
Por lo tanto, dado que el desarrollador suele ser el único que se está probando a sí mismo, la carga del servidor es muy pequeña, lo que lleva al intercambio del mismo hilo cada vez que el acceso es, lo que resulta en el malentendido: cada sesión tiene una línea de hilo.
3. Threadlocal es relativo a cada hilo. Cada vez que el usuario acceda, habrá un nuevo ThreadLocal.
Teóricamente, ThreadLocal es de hecho relativo a cada hilo, cada hilo tendrá su propio ThreadLocal. Pero como se mencionó anteriormente, los servidores de aplicaciones generales mantienen un conjunto de grupos de subprocesos. Por lo tanto, diferentes usuarios pueden recibir el mismo hilo. Por lo tanto, al hacer el principal basado en el encabezado, debe tener cuidado para evitar el caché de las variables de hilo.
4. Para cada acceso al usuario, ThreadLocal se puede usar varias veces.
Se puede decir que ThreadLocal es una espada de doble filo y puede tener muy buenos resultados si se usa. Sin embargo, si ThreadLocal no se usa bien, será lo mismo que las variables globales. El código no puede reutilizarse y no se puede probar de forma independiente. Porque algunas clases que podrían haberse reutilizado ahora dependen de la variable de ThreadLocal. Si no hay ThreadLocal, estas clases no están disponibles. Personalmente, creo que ThreadLocal se usa bien y vale la pena referirse
1. Almacene el usuario de la sesión actual: Quake Want Jert
2. Almacene algunas variables de contexto, como ActionContext de Webwork
3. Sesiones de almacenamiento, como Spring Hibernate Orm Sessions