¿Cuándo y cómo debería usar una variable ThreadLocal?

¿Cuándo debería usar una variable ThreadLocal ?

¿Cómo se usa?

Un uso posible (y común) es cuando tienes algún objeto que no es seguro para subprocesos, pero quieres evitar sincronizar el acceso a ese objeto (te estoy mirando, SimpleDateFormat ). En cambio, déle a cada hilo su propia instancia del objeto.

Por ejemplo:

 public class Foo { // SimpleDateFormat is not thread-safe, so give one to each thread private static final ThreadLocal formatter = new ThreadLocal(){ @Override protected SimpleDateFormat initialValue() { return new SimpleDateFormat("yyyyMMdd HHmm"); } }; public String formatIt(Date date) { return formatter.get().format(date); } } 

Documentación .

Dado que un ThreadLocal es una referencia a datos dentro de un ThreadLocal dado, puede terminar con ThreadLocal al usar ThreadLocal en servidores de aplicaciones que usan grupos de hilos. ThreadLocal tener mucho cuidado al limpiar cualquier ThreadLocal s get() o set() utilizando el ThreadLocal remove() ThreadLocal .

Si no realiza la limpieza cuando termina, cualquier referencia que tenga a clases cargadas como parte de una aplicación web implementada permanecerá en el montón permanente y nunca se recolectará basura. La redistribución / desinstalación de la aplicación web no eliminará la referencia de cada Thread a la (s) clase (s) de su aplicación porque el Thread no es propiedad de su aplicación web. Cada implementación sucesiva creará una nueva instancia de la clase que nunca se recolectará como basura.

Usted terminará con excepciones de memoria insuficiente debido a java.lang.OutOfMemoryError: PermGen space y después de google probablemente solo boostá -XX:MaxPermSize lugar de corregir el error.

Si termina experimentando estos problemas, puede determinar qué hilo y clase conserva estas referencias utilizando el Analizador de memoria de Eclipse y / o siguiendo la guía y seguimiento de Frank Kieviet .

Actualización: redescubrí la entrada del blog de Alex Vasseur que me ayudó a encontrar algunos problemas de ThreadLocal que estaba teniendo.

Muchos marcos usan ThreadLocals para mantener algún contexto relacionado con el hilo actual. Por ejemplo, cuando la transacción actual se almacena en ThreadLocal, no necesita pasarla como parámetro a través de cada llamada a un método, en caso de que alguien de la stack necesite acceder a ella. Las aplicaciones web pueden almacenar información sobre la solicitud y la sesión actual en un ThreadLocal, de modo que la aplicación tenga fácil acceso a ellas. Con Guice puede usar ThreadLocals al implementar ámbitos personalizados para los objetos inyectados (los ámbitos de servlets predeterminados de Guice probablemente también los utilicen).

ThreadLocals son un tipo de variables globales (aunque un poco menos malas porque están restringidas a un hilo), por lo que debes tener cuidado al usarlas para evitar efectos secundarios no deseados y pérdidas de memoria. Diseña tus API para que los valores de ThreadLocal siempre se borren automáticamente cuando ya no se necesiten y que el uso incorrecto de la API no sea posible (por ejemplo, como este ). ThreadLocals se puede utilizar para hacer que el código sea más limpio y, en algunos casos excepcionales, son la única forma de hacer que algo funcione (mi proyecto actual tenía dos casos similares, que se documentan aquí en “Campos estáticos y variables globales”).

En Java, si tiene un dato que puede variar por hilo, sus opciones son pasar ese dato a cada método que lo necesite (o lo necesite) o asociar el dato con el hilo. Pasar el dato por todas partes puede ser viable si todos sus métodos ya tienen que pasar por una variable común de “contexto”.

Si ese no es el caso, es posible que no desee agrupar las firmas de sus métodos con un parámetro adicional. En un mundo sin hilos, podría resolver el problema con el equivalente Java de una variable global. En una palabra enhebrada, el equivalente de una variable global es una variable de subproceso local.

Básicamente, cuando necesita que el valor de una variable dependa del hilo actual y no le conviene adjuntar el valor al hilo de alguna otra manera (por ejemplo, subclases del hilo).

Un caso típico es cuando algún otro marco ha creado el hilo en el que se está ejecutando su código, por ejemplo, un contenedor de servlets, o donde tiene más sentido usar ThreadLocal porque su variable está “en su lugar lógico” (en lugar de una variable colgando de una subclase Thread o en algún otro hash map).

En mi sitio web, tengo más discusión y ejemplos de cuándo usar ThreadLocal que también pueden ser de interés.

Algunas personas abogan por utilizar ThreadLocal como una forma de adjuntar una “ID de hilo” a cada hilo en ciertos algoritmos concurrentes donde se necesita un número de hilo (ver, por ejemplo, Herlihy y Shavit). En tales casos, verifique que realmente esté obteniendo un beneficio.

Hay muy buen ejemplo en el libro Java Concurrency in Practice . Donde el autor ( Joshua Bloch ) explica cómo el confinamiento del hilo es una de las formas más simples de lograr la seguridad del hilo y ThreadLocal es un medio más formal para mantener el confinamiento del hilo. Al final, también explica cómo las personas pueden abusar de él al usarlo como variables globales.

He copiado el texto del libro mencionado pero falta el código 3.10 ya que no es muy importante entender dónde se debe usar ThreadLocal.

Las variables locales de subprocesos a menudo se usan para evitar compartir en diseños basados ​​en Singleton mutables o variables globales. Por ejemplo, una aplicación de un único subproceso podría mantener una conexión de base de datos global que se inicializa al inicio para evitar tener que pasar una Conexión a cada método. Dado que las conexiones JDBC pueden no ser seguras para subprocesos, una aplicación multiproceso que utiliza una conexión global sin coordinación adicional tampoco es segura para subprocesos. Al usar un ThreadLocal para almacenar la conexión JDBC, como en ConnectionHolder en el Listado 3.10, cada hilo tendrá su propia conexión.

ThreadLocal es ampliamente utilizado en la implementación de marcos de aplicaciones. Por ejemplo, los contenedores J2EE asocian un contexto de transacción con un hilo de ejecución durante la duración de una llamada EJB. Esto se implementa fácilmente utilizando un Thread-Local estático que contiene el contexto de la transacción: cuando el código del framework necesita determinar qué transacción se está ejecutando actualmente, recupera el contexto de la transacción desde este ThreadLocal. Esto es conveniente porque reduce la necesidad de pasar información de contexto de ejecución en cada método, pero acopla cualquier código que use este mecanismo al marco.

Es fácil abusar de ThreadLocal tratando su propiedad de confinamiento de hilos como una licencia para usar variables globales o como un medio para crear argumentos de métodos “ocultos”. Al igual que las variables globales, las variables locales de subprocesos pueden restar reutilización e introducir acoplamientos ocultos entre clases, por lo que deben utilizarse con cuidado.

La documentación lo dice muy bien: “cada hilo que accede [a una variable local de hilo] (a través de su método get o set) tiene su propia copia de la variable, inicializada de manera independiente”.

Usas uno cuando cada hilo debe tener su propia copia de algo. Por defecto, los datos se comparten entre hilos.

El servidor de Webapp puede mantener un grupo de subprocesos, y una var de ThreadLocal debe eliminarse antes de la respuesta al cliente, por lo que la próxima solicitud puede reutilizar el subproceso actual.

  1. ThreadLocal en Java se introdujo en JDK 1.2 pero luego se generó en JDK 1.5 para introducir seguridad de tipo en la variable ThreadLocal.

  2. ThreadLocal se puede asociar con el ámbito del subproceso, todo el código que se ejecuta por subproceso tiene acceso a las variables ThreadLocal, pero dos subprocesos no pueden ver la otra variable ThreadLocal.

  3. Cada subproceso contiene una copia exclusiva de la variable ThreadLocal que se convierte en elegible para la recolección de elementos no utilizados después de que el hilo finalizó o murió, normalmente o debido a cualquier excepción, dado que la variable ThreadLocal no tiene ninguna otra referencia activa.

  4. Las variables ThreadLocal en Java son generalmente campos estáticos privados en Clases y mantienen su estado dentro de Thread.

Leer más: http://javarevisited.blogspot.com/2012/05/how-to-use-threadlocal-in-java-benefits.html#ixzz2XltgbHTK

Dos casos de uso donde se puede usar la variable threadlocal –
1- Cuando tenemos un requisito para asociar el estado con un hilo (por ejemplo, un ID de usuario o ID de transacción). Eso generalmente sucede con una aplicación web que cada solicitud que va a un servlet tiene un transactionID único asociado a él.

 // This class will provide a thread local variable which // will provide a unique ID for each thread class ThreadId { // Atomic integer containing the next thread ID to be assigned private static final AtomicInteger nextId = new AtomicInteger(0); // Thread local variable containing each thread's ID private static final ThreadLocal threadId = ThreadLocal.withInitial(()-> {return nextId.getAndIncrement();}); // Returns the current thread's unique ID, assigning it if necessary public static int get() { return threadId.get(); } } 

Tenga en cuenta que aquí el método withInitial se implementa utilizando la expresión lambda.
2- Otro caso de uso es cuando queremos tener una instancia de hilo seguro y no queremos utilizar la sincronización ya que el costo de rendimiento con la sincronización es mayor. Uno de estos casos es cuando se usa SimpleDateFormat. Como SimpleDateFormat no es seguro para subprocesos, tenemos que proporcionar un mecanismo para hacerlo seguro.

 public class ThreadLocalDemo1 implements Runnable { // threadlocal variable is created private static final ThreadLocal dateFormat = new ThreadLocal(){ @Override protected SimpleDateFormat initialValue(){ System.out.println("Initializing SimpleDateFormat for - " + Thread.currentThread().getName() ); return new SimpleDateFormat("dd/MM/yyyy"); } }; public static void main(String[] args) { ThreadLocalDemo1 td = new ThreadLocalDemo1(); // Two threads are created Thread t1 = new Thread(td, "Thread-1"); Thread t2 = new Thread(td, "Thread-2"); t1.start(); t2.start(); } @Override public void run() { System.out.println("Thread run execution started for " + Thread.currentThread().getName()); System.out.println("Date formatter pattern is " + dateFormat.get().toPattern()); System.out.println("Formatted date is " + dateFormat.get().format(new Date())); } } 

Tienes que tener mucho cuidado con el patrón ThreadLocal. Hay algunos aspectos negativos importantes como Phil mencionó, pero uno que no se mencionó es para asegurarse de que el código que establece el contexto de ThreadLocal no sea “reentrante”.

Pueden ocurrir cosas malas cuando el código que establece la información se ejecuta una segunda o tercera vez porque la información en su hilo puede comenzar a mutar cuando usted no lo esperaba. Así que asegúrese de que la información de ThreadLocal no se haya establecido antes de volver a establecerla.

Nada realmente nuevo aquí, pero hoy descubrí que ThreadLocal es muy útil cuando se usa Validación de Bean en una aplicación web. Los mensajes de validación están localizados, pero de forma predeterminada usan Locale.getDefault() . Puede configurar el Validator con un MessageInterpolator diferente, pero no hay forma de especificar la configuración Locale cuando llame a validate . Por lo tanto, podría crear un ThreadLocal estático (o mejor aún, un contenedor general con otras cosas que podría necesitar para ser ThreadLocal y luego hacer que su MessageInterpolator personalizado seleccione el idioma de allí. El siguiente paso es escribir un ServletFilter que use una sesión valor o request.getLocale() para elegir la configuración regional y almacenarla en su referencia de ThreadLocal .

Como se mencionó en @unknown (google), su uso es definir una variable global en la cual el valor al que se hace referencia puede ser único en cada hilo. Sus usos generalmente implican almacenar algún tipo de información contextual que esté vinculada al hilo de ejecución actual.

Lo usamos en un entorno Java EE para pasar la identidad del usuario a clases que no son conscientes de Java EE (no tienen acceso a HttpSession, ni a EJB SessionContext). De esta forma, el código, que hace uso de la identidad para operaciones basadas en seguridad, puede acceder a la identidad desde cualquier lugar, sin tener que pasarla explícitamente en cada llamada a un método.

El ciclo de solicitud / respuesta de las operaciones en la mayoría de las llamadas Java EE hace que este tipo de uso sea fácil ya que proporciona puntos de entrada y salida bien definidos para establecer y desarmar el ThreadLocal.

ThreadLocal garantizará que el acceso al objeto mutable por los múltiples hilos en el método no sincronizado esté sincronizado, significa hacer que el objeto mutable sea inmutable dentro del método.

Esto se logra dando una nueva instancia de objeto mutable para cada bash de subproceso. Entonces es una copia local para cada hilo. Este es un truco al hacer que la variable de instancia en un método sea accedida como una variable local. Como tu método consciente, la variable local solo está disponible para el hilo, una diferencia es; Las variables locales del método no estarán disponibles para el subproceso una vez que finalice la ejecución del método donde el objeto mutable compartido con threadlocal estará disponible en varios métodos hasta que lo limpiemos.

Por definición:

La clase ThreadLocal en Java le permite crear variables que solo se pueden leer y escribir con el mismo hilo. Por lo tanto, incluso si dos subprocesos están ejecutando el mismo código y el código tiene una referencia a una variable ThreadLocal, entonces los dos subprocesos no pueden ver las variables ThreadLocal de los demás.

Cada Thread en java contiene ThreadLocalMap en él.
Dónde

 Key = One ThreadLocal object shared across threads. value = Mutable object which has to be used synchronously, this will be instantiated for each thread. 

Lograr el ThreadLocal:

Ahora crea una clase contenedora para ThreadLocal que mantendrá el objeto mutable como se muestra a continuación (con o sin initialValue() ).
Ahora getter y setter de este wrapper funcionarán en la instancia de threadlocal en lugar de en el objeto mutable.

Si getter () de threadlocal no encontró ningún valor en el threadlocalmap del Thread ; luego invocará initialValue () para obtener su copia privada con respecto al hilo.

 class SimpleDateFormatInstancePerThread { private static final ThreadLocal dateFormatHolder = new ThreadLocal() { @Override protected SimpleDateFormat initialValue() { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd") { UUID id = UUID.randomUUID(); @Override public String toString() { return id.toString(); }; }; System.out.println("Creating SimpleDateFormat instance " + dateFormat +" for Thread : " + Thread.currentThread().getName()); return dateFormat; } }; /* * Every time there is a call for DateFormat, ThreadLocal will return calling * Thread's copy of SimpleDateFormat */ public static DateFormat getDateFormatter() { return dateFormatHolder.get(); } public static void cleanup() { dateFormatHolder.remove(); } } 

Ahora wrapper.getDateFormatter() llamará a threadlocal.get() y eso comprobará el currentThread.threadLocalMap contiene esta instancia (threadlocal).
En caso afirmativo, devuelva el valor (SimpleDateFormat) para la instancia threadlocal correspondiente
de lo contrario, agregue el mapa con esta instancia de threadlocal, initialValue ().

De esta manera, se logra la seguridad de la hebra en esta clase mutable; por cada hilo está trabajando con su propia instancia mutable pero con la misma instancia de ThreadLocal. Significa que todo el hilo compartirá la misma instancia de ThreadLocal como clave, pero diferentes instancias de SimpleDateFormat como valor.

https://github.com/skanagavelu/yt.tech/blob/master/src/ThreadLocalTest.java

cuando?

Cuando un objeto no es seguro para subprocesos, en lugar de la sincronización que dificulta la escalabilidad, asigne un objeto a cada subproceso y consérvelo con el scope del subproceso, que es ThreadLocal. Uno de los objetos más utilizados, pero no seguros para subprocesos, es la conexión de base de datos y JMSConnection.

Cómo ?

Un ejemplo es Spring framework utiliza ThreadLocal en gran medida para gestionar las transacciones entre bastidores manteniendo estos objetos de conexión en las variables de ThreadLocal. En el nivel alto, cuando se inicia una transacción obtiene la conexión (y deshabilita la confirmación automática) y la mantiene en ThreadLocal. en otras llamadas de DB utiliza la misma conexión para comunicarse con db. Al final, toma la conexión de ThreadLocal y confirma (o revierte) la transacción y libera la conexión.

Creo que log4j también usa ThreadLocal para mantener MDC.

ThreadLocal es útil cuando desea tener algún estado que no se deba compartir entre diferentes hilos, pero debe ser accesible desde cada hilo durante toda su vida útil.

Como ejemplo, imagine una aplicación web, donde cada solicitud recibe un hilo diferente. Imagine que para cada solicitud necesita un dato varias veces, lo cual es bastante costoso de computar. Sin embargo, esos datos pueden haber cambiado para cada solicitud entrante, lo que significa que no puede usar una memoria caché simple. Una solución simple y rápida a este problema sería tener una variable ThreadLocal que tenga acceso a esta información, por lo que debe calcularla solo una vez para cada solicitud. Por supuesto, este problema también se puede resolver sin el uso de ThreadLocal , pero lo ThreadLocal con fines ilustrativos.

Dicho esto, tenga en cuenta que ThreadLocal s es esencialmente una forma de estado global. Como resultado, tiene muchas otras implicaciones y debe usarse solo después de considerar todas las demás soluciones posibles.

Las variables locales de subprocesos a menudo se usan para evitar compartir en diseños basados ​​en Singleton mutables o variables globales.

Se puede usar en escenarios como hacer una conexión JDBC separada para cada subproceso cuando no está utilizando un grupo de conexiones.

 private static ThreadLocal connectionHolder = new ThreadLocal() { public Connection initialValue() { return DriverManager.getConnection(DB_URL); } }; public static Connection getConnection() { return connectionHolder.get(); } 

Cuando llame a getConnection, devolverá una conexión asociada con ese hilo. Lo mismo se puede hacer con otras propiedades como dateformat, contexto de transacción que no desea compartir entre hilos.

También podría haber usado variables locales para el mismo, pero estos recursos usualmente toman tiempo en la creación, por lo que no desea crearlos una y otra vez siempre que realice alguna lógica comercial con ellos. Sin embargo, los valores de ThreadLocal se almacenan en el objeto de la hebra en sí y tan pronto como la hebra se recolecta como basura, estos valores también desaparecen.

Este enlace explica muy bien el uso de ThreadLocal.

Desde el lanzamiento de Java 8, hay una forma más declarativa de inicializar ThreadLocal :

 ThreadLocal local = ThreadLocal.withInitial(() -> "init value"); 

Hasta el lanzamiento de Java 8, tenía que hacer lo siguiente:

 ThreadLocal local = new ThreadLocal(){ @Override protected String initialValue() { return "init value"; } }; 

Además, si el método de instanciación (constructor, método de fábrica) de la clase que se utiliza para ThreadLocal no toma ningún parámetro, simplemente puede usar referencias de métodos (introducido en Java 8):

 class NotThreadSafe { // no parameters public NotThreadSafe(){} } ThreadLocal container = ThreadLocal.withInitial(NotThreadSafe::new); 

Nota: La evaluación es vaga ya que está pasando java.util.function.Supplier lambda que se evalúa solo cuando se llama a ThreadLocal#get pero el valor no se evaluó previamente.

En el almacenamiento en caché, a veces tiene que calcular el mismo valor por mucho tiempo, por lo que al almacenar el último conjunto de entradas en un método y el resultado, puede acelerar el código. Al utilizar Thread Local Storage evitas tener que pensar en bloquear.

ThreadLocal es una funcionalidad especialmente aprovisionada por JVM para proporcionar un espacio de almacenamiento aislado solo para hilos. al igual que el valor de la variable de ámbito de instancia están vinculados a una instancia determinada de una clase solamente. cada objeto tiene sus únicos valores y no pueden verse el uno al otro. también lo es el concepto de variables de ThreadLocal, son locales al hilo en el sentido de instancias de objeto, otro hilo, excepto el que lo creó, no puede verlo. Mira aquí

 import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.IntStream; public class ThreadId { private static final AtomicInteger nextId = new AtomicInteger(1000); // Thread local variable containing each thread's ID private static final ThreadLocal threadId = ThreadLocal.withInitial(() -> nextId.getAndIncrement()); // Returns the current thread's unique ID, assigning it if necessary public static int get() { return threadId.get(); } public static void main(String[] args) { new Thread(() -> IntStream.range(1, 3).forEach(i -> { System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get()); })).start(); new Thread(() -> IntStream.range(1, 3).forEach(i -> { System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get()); })).start(); new Thread(() -> IntStream.range(1, 3).forEach(i -> { System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get()); })).start(); } } 

Threadlocal proporciona una manera muy fácil de lograr la reutilización de objetos sin costo.

Tuve una situación en la que varios subprocesos creaban una imagen de caché mutable, en cada notificación de actualización.

Usé un Threadlocal en cada hilo, y luego cada hilo solo necesitaría restablecer la imagen anterior y luego actualizarla nuevamente desde el caché en cada notificación de actualización.

Los objetos usuales reutilizables de las agrupaciones de objetos tienen un costo de seguridad de subprocesos asociado a ellos, mientras que este enfoque no tiene ninguno.

La clase ThreadLocal en Java le permite crear variables que solo se pueden leer y escribir con el mismo hilo. Thus, even if two threads are executing the same code, and the code has a reference to a ThreadLocal variable, then the two threads cannot see each other’s ThreadLocal variables.

Lee mas