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


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

¿Cómo se usa?


780
2018-05-03 19:59


origen


Respuestas:


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


777
2018-05-03 20:26



Desde un ThreadLocal es una referencia a los datos dentro de un determinado Thread, puedes terminar con descargas de clase al usar ThreadLocals en servidores de aplicaciones que usan grupos de subprocesos. Debe tener mucho cuidado con la limpieza de cualquier ThreadLocaleres tú get() o set() usando el ThreadLocales remove() método.

Si no limpia cuando termina, cualquier referencia que tenga a las clases cargadas como parte de una aplicación web desplegada permanecerá en el montón permanente y nunca obtendrán basura recolectada. Volver a desplegar / desinstalar la aplicación web no limpiará cada Threadreferencia de su (s) clase (s) de webapp desde el Thread no es algo propiedad de su aplicación web. Cada implementación sucesiva creará una nueva instancia de la clase que nunca se recolectará como basura.

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

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

Actualización: Re-descubierto Entrada del blog de Alex Vasseur eso me ayudó a rastrear algunos ThreadLocal problemas que estaba teniendo.


380
2018-05-03 22:02



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 pila 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 puedes usar ThreadLocals al implementar ámbitos personalizados para los objetos inyectados (por defecto de Guice ámbitos servlet muy probablemente los use también).

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 no sea posible el uso incorrecto de la API (por ejemplo, Me gusta esto) ThreadLocals se puede utilizar para hacer que el código sea más limpio, y en algunos casos excepcionales, son la única manera de hacer que algo funcione (mi proyecto actual tenía dos casos similares; están documentados). aquí en "Campos estáticos y variables globales").


123
2018-05-04 13:36



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.


42
2018-05-03 20:20



Básicamente, cuando necesitas un el valor de la variable depende del hilo actual y no es conveniente que adjunte el valor al hilo de alguna otra manera (por ejemplo, subclassing thread).

Un caso típico es donde algún otro marco ha creado el hilo que su código se está ejecutando, p. 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 que cuelga de una subclase Thread o en algún otro hash).

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

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


13
2018-05-03 23:50



Hay muy buen ejemplo en el libro Concurrencia de Java en la práctica. Donde autor (Joshua Bloch) explica cómo el confinamiento de subprocesos es una de las formas más sencillas de lograr seguridad de subprocesos y ThreadLocal es un medio más formal de 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.


11
2017-10-04 09:28



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 inicializada de la variable".

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


9
2018-05-03 20:05



El servidor Webapp puede mantener un grupo de subprocesos y un ThreadLocal var debe eliminarse antes de la respuesta al cliente, por lo que el hilo actual puede reutilizarse mediante la siguiente solicitud.


9
2018-04-28 15:22



  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.

Lee mas: http://javarevisited.blogspot.com/2012/05/how-to-use-threadlocal-in-java-benefits.html#ixzz2XltgbHTK


8
2017-07-01 06:10



Dos casos de uso donde se puede usar la variable threadlocal -
1- Cuando tenemos un requisito para asociar 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<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 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<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()));
    } 

}

6
2017-08-20 15:58



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.


4
2017-10-23 18:50