¿Cuándo y cómo debo usar una variable ThreadLocal?

877

¿Cuándo debo usar un ThreadLocal variable?

¿Cómo se usa?

Hearen
fuente
11
Si está utilizando ThreadLocal, ¡nunca escriba un envoltorio sobre él! Todos y cada uno de los desarrolladores que quieran usar la variable deben saber que es 'ThreadLocal'
no es un error
2
@Notabug Si eres explícito, puedes nombrar tu variable para que sea con el que estés tratando con un valor ThreadLocal, por ejemplo RequestUtils.getCurrentRequestThreadLocal(). Sin embargo, esto no es muy elegante, pero esto se debe al hecho de que ThreadLocal en sí no es muy elegante en la mayoría de los casos.
whirlwin
@Sasha ¿Se puede usar ThreadLocal para almacenar el seguimiento de ejecución
Gaurav

Respuestas:

864

Un uso posible (y común) es cuando tiene algún objeto que no es seguro para subprocesos, pero desea evitar la sincronización del acceso a ese objeto (lo estoy mirando, SimpleDateFormat ). En cambio, dele 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<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){
        @Override
        protected SimpleDateFormat initialValue()
        {
            return new SimpleDateFormat("yyyyMMdd HHmm");
        }
    };

    public String formatIt(Date date)
    {
        return formatter.get().format(date);
    }
}

Documentación .

pensar demasiado
fuente
160
Otra alternativa a la sincronización o threadlocal es hacer que la variable sea una variable local. Los vars locales siempre son seguros para subprocesos. Supongo que es una mala práctica hacer que DateFormats sea local porque son caros de crear, pero nunca he visto métricas sólidas sobre este tema.
Julien Chastang
18
Este es un alto precio a pagar por la piratería SimpleDateFormat. Quizás sea mejor usar una alternativa segura para subprocesos . Si está de acuerdo en que los singletons son malos, entonces ThreadLocales aún peor.
Alexander Ryzhov
44
@overthink ¿Hay alguna razón para declarar ThreadLocal estático y final, me refiero al rendimiento o algo así?
Sachin Gorade
44
El método ThreadLocal.get () llamará a ThreadLocal.initialValue () (una vez) para cada subproceso, lo que significa que se crea un objeto SimpleDateFormat para cada subproceso. ¿No es mejor simplemente tener SimpleDateFormat como variables locales (ya que no tenemos que lidiar con problemas de recolección de basura)?
sudeepdino008
3
Solo tenga cuidado de no usar la inicialización de doble llave para su SimpleDateFormat porque eso crearía una clase anónima para que su cargador de clases no se pueda recolectar basura. pérdida de memoria: devuelva el nuevo SimpleDateFormat () {{applyPattern ("aaaaMMdd HHmm")}};
user1944408
427

Como a ThreadLocales una referencia a los datos dentro de un determinado Thread, puede terminar con pérdidas de carga de clase cuando se usa ThreadLocals en servidores de aplicaciones que usan grupos de subprocesos. Tienes que ser muy cuidadosos con la limpieza de cualquier ThreadLocalque S get()o set()mediante el uso de los ThreadLocal'sremove() método.

Si no limpia cuando haya terminado, cualquier referencia que contenga a clases cargadas como parte de una aplicación web desplegada permanecerá en el montón permanente y nunca se recolectará basura. Volver a desplegar / desplegar la aplicación web no limpiará Threadla referencia de cada uno a la (s) clase (s) de su aplicación web ya Threadque no es algo propiedad de su aplicación web. Cada implementación sucesiva creará una nueva instancia de la clase que nunca se recolectará basura.

Terminará con las excepciones de memoria insuficiente debido a que, java.lang.OutOfMemoryError: PermGen spacedespués de buscar en Google, probablemente solo aumente en -XX:MaxPermSizelugar de corregir el error.

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

Actualización: Re-descubrí la entrada del blog de Alex Vasseur que me ayudó a localizar algunos ThreadLocalproblemas que estaba teniendo.

Phil M
fuente
11
Alex Vasseur movió su blog. Aquí hay un enlace actual al artículo de pérdida de memoria.
Kenster
12
Parece que el hilo al que Julien enlaza se ha trasladado hasta aquí , y vale la pena leerlo ...
Donal Fellows
28
Esta es una gran cantidad de votos a favor para una respuesta que, si bien es informativa, de ninguna manera responde a la pregunta.
Robin
8
Ahora que Java 8 mata a PermGen, ¿esto cambia esta respuesta de alguna manera?
Mal Lavadora
13
@ Robin: no estoy de acuerdo. La pregunta es sobre cómo usar ThreadLocal correctamente, para obtener una comprensión completa de cualquier concepto (ThreadLocal aquí) también es importante entender cómo no usarlo y los riesgos de usarlo descuidadamente, y de eso se trata la respuesta de Phil. Me alegra que haya cubierto ese punto que no está cubierto en ninguna otra respuesta. Todos esos votos son bien merecidos. Creo que SO debería desarrollar la comprensión de los conceptos en lugar de ser solo un sitio de control de calidad.
Saurabh Patil
166

Muchos marcos usan ThreadLocals para mantener un contexto relacionado con el hilo actual. Por ejemplo, cuando la transacción actual se almacena en un ThreadLocal, no necesita pasarla como un parámetro a través de cada llamada al método, en caso de que alguien de la pila necesite acceder a ella. Las aplicaciones web pueden almacenar información sobre la solicitud actual y la sesión en un ThreadLocal, para 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 servlet predeterminados de Guice probablemente también los usen).

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

Esko Luontola
fuente
44
Así es exactamente como el marco exPOJO (www.expojo.com) permite el acceso a ORM Session / PersistenceManager sin necesidad de sobrecarga de anotaciones e inyecciones. Es algo así como 'inyección de hilo' en lugar de 'inyección de objeto'. Proporciona acceso a las dependencias sin el requisito (y la sobrecarga) de tenerlas incrustadas en cada objeto que pueda necesitar esas dependencias. Es sorprendente cómo 'ligero' puede hacer un marco DI cuando usa inyección de hilo en lugar de DI clásico (por ejemplo, primavera, etc.)
Volksman
27
¿Por qué tuve que desplazarme hacia abajo para encontrar esta respuesta esencial?
Jeremy Stein
1
@Esko, en su proyecto, su uso de ThreadLocal en hashcode e igual es innecesario. Un mejor enfoque es crear o pasar una referencia a una función hashcode / equals cuando sea necesario. De esta forma, puede evitar por completo la necesidad de hackear ThreadLocal.
Pacerier
1
@JeremyStein, las mejores respuestas están en stackoverflow.com/a/817911/632951 y stackoverflow.com/a/17398338/632951 y stackoverflow.com/a/818364/632951
Pacerier el
1
Esta es la mejor explicación del uso de TL.
Dexter
52

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 pueda necesitarlo), o asociar el dato con el hilo. Pasar el dato por todas partes puede ser viable si todos sus métodos ya necesitan pasar una variable de "contexto" común.

Si ese no es el caso, es posible que no desee saturar 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 hilo local.

usuario100464
fuente
13
Por lo tanto, debe evitar los subprocesos locales de la misma manera que evita los globales. No puedo aceptar que esté bien crear globales (locales de subprocesos) en lugar de pasar valores, a la gente no le gusta porque a menudo revela problemas de arquitectura que no quieren solucionar.
Juan Mendes
10
Tal vez ... Sin embargo, puede ser útil si tiene una base de código enorme y existente a la que debe agregar un nuevo dato que debe pasarse a todas partes , por ejemplo, un contexto de sesión, transacción de db, usuario conectado, etc. .
Cornel Masson
66
Felicitaciones por usar datos en lugar de datos.
profundo
Esto me hizo sentir curiosidad. 'datum' is the singular form and 'data' is the plural form.
Siddhartha
23

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

Copié 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 Singletons mutables o variables globales. Por ejemplo, una aplicación de subproceso único puede 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 se usa ampliamente en la implementación de marcos de aplicaciones. Por ejemplo, los contenedores J2EE asocian un contexto de transacción con un subproceso en ejecución durante la duración de una llamada EJB. Esto se implementa fácilmente usando un Thread-Local estático que contiene el contexto de la transacción: cuando el código marco necesita determinar qué transacción se está ejecutando actualmente, obtiene el contexto de la transacción de este ThreadLocal. Esto es conveniente porque reduce la necesidad de pasar información de contexto de ejecución a cada método, pero combina 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 valor a la reutilización e introducir acoplamientos ocultos entre las clases y, por lo tanto, deben usarse con cuidado.

Rakesh Chauhan
fuente
18

Esencialmente, cuando necesita el valor de una variable para depender del subproceso actual y no le conviene adjuntar el valor al subproceso de alguna otra manera (por ejemplo, subclasificar subprocesos).

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 servlet, 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 de Thread o en algún otro mapa hash).

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 usar ThreadLocal como una forma de adjuntar un "ID de subproceso" a cada subproceso en ciertos algoritmos concurrentes donde necesita un número de subproceso (ver, por ejemplo, Herlihy y Shavit). En tales casos, compruebe que realmente está obteniendo un beneficio.

Neil Coffey
fuente
¿Se puede usar ThreadLocal para almacenar trazas de ejecución
Gaurav
13
  1. ThreadLocal en Java se había introducido en JDK 1.2, pero luego se generó en JDK 1.5 para introducir la seguridad de tipos en la variable ThreadLocal.

  2. ThreadLocal se puede asociar con el alcance de Thread, todo el código que ejecuta Thread tiene acceso a las variables ThreadLocal, pero dos hilos no pueden ver la variable ThreadLocal.

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

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

Leer más: ThreadLocal en Java - Ejemplo de programa y tutorial

Abhijit Gaikwad
fuente
¿Se puede usar ThreadLocal para almacenar trazas de ejecución
Gaurav
11

La documentación dice muy bien: "cada subproceso que accede a [una variable de subproceso local] (a través de su método get o set) tiene su propia copia de la variable inicializada independientemente".

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

RichieHindle
fuente
19
Por defecto, se comparten objetos estáticos, u objetos pasados ​​explícitamente entre hilos donde ambos hilos poseen una referencia al mismo objeto. Los objetos declarados localmente en un hilo no se comparten (son locales en la pila del hilo). Solo quería aclarar eso.
Ian Varley
@ IanVarley: Gracias por señalar eso. Entonces, si tenemos una variable declarada y utilizada en la clase MyThread, ¿necesitamos ThreadLocal en ese caso? Ejemplo: clase MyThread extiende Thread {String var1 ;, int var2; } en este caso, var1 y var2 serán parte de la propia pila de Thread y no se comparten, entonces, ¿necesitamos ThreadLocal en este caso? Podría estar totalmente equivocado en mi entendimiento, por favor guía.
Jayesh
44
Si cada subproceso quiere tener su propia copia de algo, ¿por qué no puede simplemente declararse local (que siempre es seguro para subprocesos)?
sudeepdino008
@ sudeepdino008 no siempre tiene el control de la creación de esos subprocesos (marcos de aplicaciones web, bibliotecas de subprocesos de hilos)
JMess
10

El servidor Webapp puede mantener un grupo de subprocesos, y se ThreadLocaldebe eliminar un var antes de la respuesta al cliente, por lo que la siguiente solicitud puede reutilizar el subproceso actual.

znlyj
fuente
8

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

// 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<Integer> threadId =
        ThreadLocal.<Integer>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 segura para subprocesos y no queremos usar la sincronización ya que el costo de rendimiento con la sincronización es mayor. Uno de esos casos es cuando se usa SimpleDateFormat. Como SimpleDateFormat no es seguro para subprocesos, debemos proporcionar un mecanismo para que sea seguro para subprocesos.

public class ThreadLocalDemo1 implements Runnable {
    // threadlocal variable is created
    private static final ThreadLocal<SimpleDateFormat> dateFormat = new ThreadLocal<SimpleDateFormat>(){
        @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()));
    } 

}
infoj
fuente
Todavía no vi el beneficio de usar ThreadLocal para generar la identificación única en el ejemplo uno si todo su propósito es devolver un valor entero único incremental. `` `public class ThreadId {// Entero atómico que contiene el siguiente ID de subproceso que se le asignará privado estático final AtomicInteger nextId = new AtomicInteger (0); // Devuelve el ID único del hilo actual, asignándolo si es necesario public static int get () {return nextId..getAndIncrement (); }} `` `
dashenswen
7

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

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

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

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

Además, si el método de creación de instancias (constructor, método de fábrica) de la clase utilizada ThreadLocalno toma ningún parámetro, simplemente puede usar referencias de métodos (introducidas en Java 8):

class NotThreadSafe {
    // no parameters
    public NotThreadSafe(){}
}

ThreadLocal<NotThreadSafe> container = ThreadLocal.withInitial(NotThreadSafe::new);

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

Andrii Abramov
fuente
5

Tienes que tener mucho cuidado con el patrón ThreadLocal. Hay algunos inconvenientes importantes como Phil mencionó, pero uno que no se mencionó es asegurarse de que el código que configura el contexto ThreadLocal no sea "reentrante".

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

Jeff Richley
fuente
2
El reencuentro no es un problema si el código está preparado para manejarlo. En la entrada, tome nota de si la variable ya está configurada y, al salir, restaure su valor anterior (si lo hubiera) o elimínelo (si no).
supercat
1
@Jeff, amigo, eso es cierto para cada código que escribes, no solo para el ThreadLocalpatrón. Si lo hace F(){ member=random(); F2(); write(member); }y F2 anula al miembro con un nuevo valor, entonces obviamente write(member)ya no escribirá el número que ha random()editado. Esto es literalmente solo sentido común. Del mismo modo, si lo hace F(){ F(); }, ¡ tenga suerte con su bucle infinito! Esto es cierto en todas partes y no es específico para ThreadLocal.
Pacerier
@Jeff, ejemplo de código?
Pacerier
5

cuando?

Cuando un objeto no es seguro para subprocesos, en lugar de la sincronización que dificulta la escalabilidad, dele un objeto a cada subproceso y manténgalo dentro del alcance del subproceso, que es ThreadLocal. Uno de los objetos más utilizados pero no seguros para subprocesos es Database Connection y JMSConnection.

Cómo ?

Un ejemplo es que Spring Framework usa ThreadLocal en gran medida para administrar transacciones detrás de escena manteniendo estos objetos de conexión en las variables ThreadLocal. En un 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 db, usa 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.

Caliza
fuente
5

ThreadLocal es útil cuando desea tener un estado que no debería compartirse entre diferentes subprocesos, pero debería ser accesible desde cada subproceso durante toda su vida útil.

Como ejemplo, imagine una aplicación web, donde cada solicitud es atendida por un hilo diferente. Imagine que para cada solicitud necesita un dato varias veces, lo cual es bastante costoso de calcular. 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 para este problema sería tener una ThreadLocalvariable que tenga acceso a estos datos, de modo que tenga que calcularlos solo una vez para cada solicitud. Por supuesto, este problema también se puede resolver sin el uso deThreadLocal , pero lo ideé con fines ilustrativos.

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

Dimos
fuente
ThreadLocals NO es un estado global a menos que lo conviertas en un estado global. Son recursos de pila prácticamente siempre accesibles. Puede simular un subproceso local simplemente pasando esa variable en todos sus métodos (que se retrasa); eso no lo convierte en un estado global ...
Enerccio
Es una forma de estado global, en el sentido de que es accesible desde cualquier parte del código (en el contexto del mismo hilo). Esto viene con todas las repercusiones, como no poder razonar sobre quién lee y escribe este valor. Usar un parámetro de función no es algo retrasado, es una buena práctica promover interfaces limpias. Sin embargo, estoy de acuerdo con usted en que pasar un parámetro en toda la profundidad de su base de código es un olor a código. Pero, también creo que en muchas ocasiones el uso de ThreadLocal es un olor a código en primer lugar que lo llevó aquí, así que esto es lo que uno debe reconsiderar.
Dimos
Simplemente puede ser parte del estado del objeto, no tiene que usarse globalmente. Claro, obtienes un poco de sobrecarga, pero si varios objetos tienen que tener un estado diferente por hilo, puedes usarlo ThreadLocalcomo campo de objeto ...
Enerccio
Tienes razón. Probablemente me he topado con varios usos incorrectos de ThreadLocal, donde era accesible a nivel mundial. Como dijiste, todavía se puede usar como un campo de clase que limita la visibilidad.
Dimos
4

No hay nada realmente nuevo aquí, pero descubrí hoy que ThreadLocales muy útil cuando se usa Bean Validation en una aplicación web. Los mensajes de validación están localizados, pero de forma predeterminada se usan Locale.getDefault(). Puede configurar el Validatorcon un diferente MessageInterpolator, pero no hay forma de especificar Localecuándo llamar validate. Por lo tanto, puede crear un archivo estático ThreadLocal<Locale>(o mejor aún, un contenedor general con otras cosas que podría necesitar ser ThreadLocaly luego hacer que su MessageInterpolatorselección personalizada Localede eso. El siguiente paso es escribir un ServletFilterque use un valor de sesión o request.getLocale()elegir la configuración regional y almacenar en tu ThreadLocalreferencia.

Colselaw
fuente
4

Como mencionó @unknown (google), su uso es definir una variable global en la que 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 actual de ejecución.

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 o al EJB SessionContext). De esta manera, el código, que utiliza la identidad para operaciones basadas en la seguridad, puede acceder a la identidad desde cualquier lugar, sin tener que pasarla explícitamente en cada llamada al método.

El ciclo de operaciones de solicitud / respuesta 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 configurar y desactivar ThreadLocal.

Robin
fuente
4

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

Esto se logra dando una nueva instancia de objeto mutable para cada subproceso intente acceder a él. Por lo tanto, es una copia local en cada hilo. Este es un truco para hacer que la instancia sea variable en un método al que se pueda acceder como una variable local. Como sabe, la variable local del método 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 un 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 pueden ser leídas y escritas por el mismo hilo. Por lo tanto, incluso si dos hilos están ejecutando el mismo código, y el código tiene una referencia a una variable ThreadLocal, entonces los dos hilos no pueden ver las variables ThreadLocal del otro.

Cada uno Threaden Java contiene ThreadLocalMapen é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.

Logrando el hilo Local:

Ahora cree una clase de contenedor para ThreadLocal que contendrá el objeto mutable como se muestra a continuación (con o sin initialValue()).
Ahora getter y setter de este contenedor funcionarán en una instancia de threadlocal en lugar de un objeto mutable.

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

class SimpleDateFormatInstancePerThread {

    private static final ThreadLocal<SimpleDateFormat> dateFormatHolder = new ThreadLocal<SimpleDateFormat>() {

        @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á threadlocal.get()y eso comprobará que currentThread.threadLocalMapcontiene esta instancia (threadlocal).
En caso afirmativo, devuelva el valor (SimpleDateFormat) para la instancia de threadlocal correspondiente; de ​​lo
contrario, agregue el mapa con esta instancia de threadlocal, initialValue ().

Con esto se logra la seguridad del hilo 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 que la clave, pero diferente instancia de SimpleDateFormat como valor.

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

Kanagavelu Sugumar
fuente
3

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

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

private static ThreadLocal<Connection> connectionHolder
           = new ThreadLocal<Connection>() {
      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 subproceso. Lo mismo se puede hacer con otras propiedades como dateformat, contexto de transacción que no desea compartir entre subprocesos.

También podría haber utilizado variables locales para el mismo, pero estos recursos generalmente requieren tiempo en la creación, por lo que no desea crearlos una y otra vez cada vez que realice alguna lógica comercial con ellos. Sin embargo, los valores ThreadLocal se almacenan en el propio objeto de subproceso y, tan pronto como se recoge el subproceso, estos valores también desaparecen.

Este enlace explica muy bien el uso de ThreadLocal.

bpjoshi
fuente
2
En este ejemplo, hay un problema importante: ¿Quién es responsable del cierre de la conexión? En lugar de crear esta conexión como valor inicial, deje que el consumidor de la conexión cree la conexión explícitamente y la una al hilo a través de ThreadLocal. El creador de la conexión también es responsable del cierre. Esto no está claro en este ejemplo. Crear y vincular la conexión también se puede ocultar en un marco simple mediante algo como Transaction.begin () y Transaction.end ().
Rick-Rainer Ludwig
2

La clase ThreadLocal en Java le permite crear variables que solo pueden ser leídas y escritas por el mismo hilo. Por lo tanto, incluso si dos hilos están ejecutando el mismo código, y el código tiene una referencia a una variable ThreadLocal, entonces los dos hilos no pueden ver las variables ThreadLocal del otro.

Lee mas

Pritesh Patel
fuente
2

Hay 3 escenarios para usar un ayudante de clase como SimpleDateFormat en código multiproceso, el mejor es usar ThreadLocal

Escenarios

1- Uso de un objeto compartido similar con la ayuda del mecanismo de bloqueo o sincronización que hace que la aplicación sea lenta

2- Usar como un objeto local dentro de un método

En este escenario, si tenemos 4 hilos cada uno llama a un método 1000 veces, entonces tenemos
4000 objetos SimpleDateFormat creados y esperando que GC los borre

3- Usando ThreadLocal

si tenemos 4 subprocesos y le damos a cada subproceso una instancia de SimpleDateFormat
para que tengamos 4 subprocesos , 4 objetos de SimpleDateFormat.

No hay necesidad de mecanismo de bloqueo y creación y destrucción de objetos. (Buena complejidad temporal y complejidad espacial)

mohsen.nour
fuente
2

[Para referencia] ThreadLocal no puede resolver problemas de actualización de objetos compartidos. Se recomienda utilizar un objeto staticThreadLocal que todas las operaciones comparten en el mismo hilo. El método [obligatorio] remove () debe ser implementado por las variables ThreadLocal, especialmente cuando se usan grupos de subprocesos en los que los subprocesos a menudo se reutilizan. De lo contrario, puede afectar la lógica empresarial posterior y causar problemas inesperados, como pérdida de memoria.

Joven
fuente
1

Almacenamiento en caché, en algún momento debe calcular el mismo valor 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 usar Thread Local Storage, evitas tener que pensar en bloquear.

Ian Ringrose
fuente
1

ThreadLocal es una funcionalidad especialmente aprovisionada por JVM para proporcionar un espacio de almacenamiento aislado solo para subprocesos. como el valor de la variable de ámbito de instancia, están vinculados a una instancia dada de una clase solamente. cada objeto tiene sus únicos valores y no pueden verse entre sí. también lo es el concepto de variables ThreadLocal, que son locales al hilo en el sentido de instancias de objeto que 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<Integer> 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();

}
}
Ajay Kumar
fuente
1

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

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 reutilizables habituales de los grupos de objetos tienen un costo de seguridad de subprocesos asociado a ellos, mientras que este enfoque no tiene ninguno.

Dev Amitabh
fuente
0

Pruebe este pequeño ejemplo para tener una idea de la variable ThreadLocal:

public class Book implements Runnable {
    private static final ThreadLocal<List<String>> WORDS = ThreadLocal.withInitial(ArrayList::new);

    private final String bookName; // It is also the thread's name
    private final List<String> words;


    public Book(String bookName, List<String> words) {
        this.bookName = bookName;
        this.words = Collections.unmodifiableList(words);
    }

    public void run() {
        WORDS.get().addAll(words);
        System.out.printf("Result %s: '%s'.%n", bookName, String.join(", ", WORDS.get()));
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(new Book("BookA", Arrays.asList("wordA1", "wordA2", "wordA3")));
        Thread t2 = new Thread(new Book("BookB", Arrays.asList("wordB1", "wordB2")));
        t1.start();
        t2.start();
    }
}


Salida de la consola, si el hilo BookA se realiza primero:
Libro de resultados A: 'wordA1, wordA2, wordA3'.
Libro de resultadosB: 'palabraB1, palabraB2'.

Salida de la consola, si el hilo BookB se hace primero:
Libro de resultadosB: 'wordB1, wordB2'.
Libro de resultados A: 'wordA1, wordA2, wordA3'.

DigitShifter
fuente