Pregunta

¿Cuándo debo usar un ThreadLocal ¿variable?

¿Cómo se usa?

¿Fue útil?

Solución

Un uso posible (y común) es cuando tiene algún objeto que no es seguro para subprocesos, pero desea evitar sincronizando el 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 .

Otros consejos

Desde un ThreadLocal es una referencia a datos dentro de un determinado Thread, puede terminar con fugas de carga de clases al usar ThreadLocals en servidores de aplicaciones que utilizan grupos de subprocesos.Debe tener mucho cuidado al limpiar cualquier ThreadLocaleres tu get() o set() usando el ThreadLocal's remove() método.

Si no limpia cuando haya terminado, cualquier referencia que contenga a clases cargadas como parte de una aplicación web implementada permanecerá en el archivo montón permanente y nunca conseguiremos que se recoja la basura.Volver a implementar/anular la implementación de la aplicación web no limpiará cada uno Threadla referencia a las clases de su aplicación web desde el Thread no es algo que pertenezca a su aplicación web.Cada implementación sucesiva creará una nueva instancia de la clase que nunca será recolectada como basura.

Terminará con excepciones de falta de memoria debido a java.lang.OutOfMemoryError: PermGen space y después de buscar en Google probablemente aumentará -XX:MaxPermSize en lugar de corregir el error.

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

Actualizar:Redescubierto Entrada del blog de Alex Vasseur eso me ayudó a localizar algunos ThreadLocal problemas que estaba teniendo.

Muchos frameworks usan ThreadLocals para mantener algún 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 (el valor predeterminado de Guice < a href = "https://github.com/google/guice/wiki/Scopes" rel = "nofollow noreferrer"> ámbitos de servlet probablemente también los use).

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 que el uso incorrecto de la API no sea posible (por ejemplo, aquí bajo " 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 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 un & Quot; context & Quot; variable.

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

Hay muy buen ejemplo en el libro. Concurrencia de Java en la práctica.Donde autor (Josué Bloch) explica cómo el confinamiento de subprocesos es una de las formas más sencillas de lograr la seguridad de los subprocesos y Hilo local es un medio más formal para mantener el confinamiento del hilo.Al final, también explica cómo la gente puede abusar de él usándolo como variable global.

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 se utilizan a menudo para evitar que se compartan diseños basados ​​en Singletons mutables o variables globales.Por ejemplo, una aplicación de un solo 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 utilizar 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 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 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 del contexto de ejecución a cada método, pero acopla cualquier código que utilice este mecanismo al marco.

Es fácil abusar de ThreadLocal al tratar su propiedad de confinamiento de subprocesos 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 clases y, por lo tanto, deben usarse con cuidado.

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

Un caso típico es cuando algún otro marco ha creado el hilo en el que se está ejecutando su código, p. un contenedor de servlet, o donde tiene más sentido usar ThreadLocal porque su variable es entonces & "; en su lugar lógico &"; (en lugar de una variable que cuelga de una subclase de Thread o en algún otro mapa hash).

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

Algunas personas abogan por usar ThreadLocal como una forma de adjuntar un " ID de hilo " a cada subproceso en ciertos algoritmos concurrentes donde necesita un número de subproceso (consulte, por ejemplo, Herlihy & amp; Shavit). ¡En tales casos, verifique que realmente esté obteniendo un beneficio!

La documentación lo 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 inicializada independientemente de la variable " ;.

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

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

  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 puede asociarse con el alcance de Thread, todo el código ejecutado por Thread tiene acceso a las variables de ThreadLocal, pero dos hilos no pueden ver la variable ThreadLocal de los demás.

  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 activa.

  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

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, 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()));
    } 

}

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 que se usa para java.util.function.Supplier no toma ningún parámetro, simplemente puede usar referencias de método (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 ThreadLocal#get lambda que se evalúa solo cuando se llama a <=> pero el valor no se evaluó previamente.

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.

¿cuándo?

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 al mantener estos objetos de conexión en las variables de 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.

ThreadLocal es útil cuando desea tener un estado que no se debe compartir entre diferentes subprocesos, pero debe 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 a este problema sería tener una <=> variable 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 de <=>, pero lo ideé con fines ilustrativos.

Dicho esto, tenga en cuenta que los <=> s 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.

No hay nada realmente nuevo aquí, pero hoy descubrí que ThreadLocal Es muy útil cuando se utiliza Bean Validation en una aplicación web.Los mensajes de validación están localizados, pero de forma predeterminada se utilizan Locale.getDefault().Puedes configurar el Validator con un diferente MessageInterpolator, pero no hay manera de especificar el Locale cuando usted llama validate.Entonces podrías crear una estática. ThreadLocal<Locale> (o mejor aún, un contenedor general con otras cosas que quizás necesites ThreadLocal y luego tener tu costumbre MessageInterpolator elige el Locale a partir de ese.El siguiente paso es escribir un ServletFilter que utiliza un valor de sesión o request.getLocale() para elegir la ubicación y guardarla en su ThreadLocal referencia.

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 facilita este tipo de uso, ya que proporciona puntos de entrada y salida bien definidos para establecer y desactivar ThreadLocal.

ThreadLocal garantizará el acceso al objeto mutable mediante el hilos en el método no sincronizado está sincronizado, lo que significa que El objeto mutable será inmutable dentro del método.

Éste se logra dando una nueva instancia de objeto mutable para cada subproceso Intente acceder a él.Entonces es una copia local de cada hilo.Esto es algo hackear la creación de una variable de instancia en un método al que se puede acceder como un variable local.Como sabes, la variable local del método solo está disponible Para el hilo, una diferencia es;Las variables locales del método no disponible para el subproceso una vez finalizada la ejecución del método, donde como mutable objeto 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 pueden solo pueden ser leídos y escritos 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 un ThreadLocal, entonces los dos subprocesos no pueden verse entre sí ThreadVariables locales.

Cada Thread en java contiene ThreadLocalMap en eso.
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 cree una clase contenedora para ThreadLocal que contendrá el objeto mutable como se muestra a continuación (con o sin initialValue()).
Ahora el captador y definidor de este contenedor funcionará en una instancia local de subprocesos en lugar de en un objeto mutable.

Si getter() de threadlocal no encontró ningún valor en el threadlocalmap del Thread;luego invocará el valor inicial () 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á el currentThread.threadLocalMap contiene este Instancia (threadlocal).
En caso afirmativo, devuelva el valor (SimpleDateFormat) para la instancia local de subproceso correspondiente.
de lo contrario, agregue el mapa con esta instancia local de subprocesos, valorinicial().

Con esto se logra la seguridad de subprocesos en esta clase mutable;por cada hilo está trabajando con su propia instancia mutable pero con la misma instancia ThreadLocal.Significa que todo el hilo compartirá la misma instancia de ThreadLocal como clave, pero una instancia de SimpleDateFormat diferente como valor.

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

Las variables locales de subproceso se utilizan a menudo para evitar el uso compartido en diseños basados en Singletons mutables o variables globales.

Se puede utilizar en escenarios como realizar una conexión JDBC separada para cada subproceso cuando no se utiliza un grupo 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 llama a getConnection, devolverá una conexión asociada con ese hilo. Lo mismo se puede hacer con otras propiedades como el formato de fecha y el contexto de transacción que no desea compartir entre hilos.

También podría haber usado variables locales para lo mismo, pero estos recursos generalmente toman tiempo en su creación, por lo que no desea crearlos una y otra vez cada vez que realiza alguna lógica de negocios con ellos.Sin embargo, los valores de ThreadLocal se almacenan en el propio objeto del subproceso y, tan pronto como el subproceso se recolecta como basura, estos valores también desaparecen.

Este enlace Explica muy bien el uso de ThreadLocal.

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.

Leer más

[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.

Almacenamiento en caché, en algún momento debe calcular el mismo valor mucho tiempo, por lo que almacenando 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.

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 las variables ThreadLocal, son locales al hilo en el sentido de que las instancias de objeto otro hilo, excepto el que lo creó, no puede verlo. Ver 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();

}
}

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.

Utilicé 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 las agrupaciones de objetos tienen un costo de seguridad de subprocesos asociado a ellos, mientras que este enfoque no tiene ninguno.

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

Escenarios

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

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

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

3- Uso de ThreadLocal

si tenemos 4 hilos y le damos a cada hilo una instancia SimpleDateFormat
 entonces tenemos 4 hilos , 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)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top