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:
publicclassFoo{// SimpleDateFormat is not thread-safe, so give one to each threadprivatestaticfinalThreadLocal<SimpleDateFormat> formatter =newThreadLocal<SimpleDateFormat>(){@OverrideprotectedSimpleDateFormat initialValue(){returnnewSimpleDateFormat("yyyyMMdd HHmm");}};publicString formatIt(Date date){return formatter.get().format(date);}}
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.
@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.
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").
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.
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.
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.
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).
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.
¿Se puede usar ThreadLocal para almacenar trazas de ejecución
Gaurav
13
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.
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.
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.
Las variables ThreadLocal en Java son generalmente campos estáticos privados en las clases y mantienen su estado dentro de Thread.
¿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.
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.
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 threadclassThreadId{// Atomic integer containing the next thread ID to be assignedprivatestaticfinalAtomicInteger nextId =newAtomicInteger(0);// Thread local variable containing each thread's IDprivatestaticfinalThreadLocal<Integer> threadId =ThreadLocal.<Integer>withInitial(()->{return nextId.getAndIncrement();});// Returns the current thread's unique ID, assigning it if necessarypublicstaticint 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.
publicclassThreadLocalDemo1implementsRunnable{// threadlocal variable is createdprivatestaticfinalThreadLocal<SimpleDateFormat> dateFormat =newThreadLocal<SimpleDateFormat>(){@OverrideprotectedSimpleDateFormat initialValue(){System.out.println("Initializing SimpleDateFormat for - "+Thread.currentThread().getName());returnnewSimpleDateFormat("dd/MM/yyyy");}};publicstaticvoid main(String[] args){ThreadLocalDemo1 td =newThreadLocalDemo1();// Two threads are createdThread t1 =newThread(td,"Thread-1");Thread t2 =newThread(td,"Thread-2");
t1.start();
t2.start();}@Overridepublicvoid 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(newDate()));}}
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 =newThreadLocal<String>(){@OverrideprotectedString 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):
classNotThreadSafe{// no parameterspublicNotThreadSafe(){}}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.
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.
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.
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.
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.
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.
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=OneThreadLocal 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.
classSimpleDateFormatInstancePerThread{privatestaticfinalThreadLocal<SimpleDateFormat> dateFormatHolder =newThreadLocal<SimpleDateFormat>(){@OverrideprotectedSimpleDateFormat initialValue(){SimpleDateFormat dateFormat =newSimpleDateFormat("yyyy-MM-dd"){
UUID id = UUID.randomUUID();@OverridepublicString 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
*/publicstaticDateFormat getDateFormatter(){return dateFormatHolder.get();}publicstaticvoid 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.
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.
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.
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)
[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 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.
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;publicclassThreadId{privatestaticfinalAtomicInteger nextId =newAtomicInteger(1000);// Thread local variable containing each thread's IDprivatestaticfinalThreadLocal<Integer> threadId =ThreadLocal.withInitial(()-> nextId.getAndIncrement());// Returns the current thread's unique ID, assigning it if necessarypublicstaticint get(){return threadId.get();}publicstaticvoid main(String[] args){newThread(()->IntStream.range(1,3).forEach(i ->{System.out.println(Thread.currentThread().getName()+" >> "+newThreadId().get());})).start();newThread(()->IntStream.range(1,3).forEach(i ->{System.out.println(Thread.currentThread().getName()+" >> "+newThreadId().get());})).start();newThread(()->IntStream.range(1,3).forEach(i ->{System.out.println(Thread.currentThread().getName()+" >> "+newThreadId().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.
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.
Pruebe este pequeño ejemplo para tener una idea de la variable ThreadLocal:
publicclassBookimplementsRunnable{privatestaticfinalThreadLocal<List<String>> WORDS =ThreadLocal.withInitial(ArrayList::new);privatefinalString bookName;// It is also the thread's nameprivatefinalList<String> words;publicBook(String bookName,List<String> words){this.bookName = bookName;this.words =Collections.unmodifiableList(words);}publicvoid run(){
WORDS.get().addAll(words);System.out.printf("Result %s: '%s'.%n", bookName,String.join(", ", WORDS.get()));}publicstaticvoid main(String[] args){Thread t1 =newThread(newBook("BookA",Arrays.asList("wordA1","wordA2","wordA3")));Thread t2 =newThread(newBook("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'.
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.Respuestas:
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:
Documentación .
fuente
SimpleDateFormat
. Quizás sea mejor usar una alternativa segura para subprocesos . Si está de acuerdo en que los singletons son malos, entoncesThreadLocal
es aún peor.Como a
ThreadLocal
es una referencia a los datos dentro de un determinadoThread
, puede terminar con pérdidas de carga de clase cuando se usaThreadLocal
s en servidores de aplicaciones que usan grupos de subprocesos. Tienes que ser muy cuidadosos con la limpieza de cualquierThreadLocal
que Sget()
oset()
mediante el uso de losThreadLocal
'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á
Thread
la referencia de cada uno a la (s) clase (s) de su aplicación web yaThread
que 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 space
después de buscar en Google, probablemente solo aumente en-XX:MaxPermSize
lugar 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
ThreadLocal
problemas que estaba teniendo.fuente
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").
fuente
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.
fuente
'datum' is the singular form and 'data' is the plural form.
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.
fuente
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.
fuente
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.
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.
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.
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
fuente
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.
fuente
El servidor Webapp puede mantener un grupo de subprocesos, y se
ThreadLocal
debe eliminar un var antes de la respuesta al cliente, por lo que la siguiente solicitud puede reutilizar el subproceso actual.fuente
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.
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.
fuente
Desde el lanzamiento de Java 8, hay una forma más declarativa de inicializar
ThreadLocal
:Hasta el lanzamiento de Java 8, tenía que hacer lo siguiente:
Además, si el método de creación de instancias (constructor, método de fábrica) de la clase utilizada
ThreadLocal
no toma ningún parámetro, simplemente puede usar referencias de métodos (introducidas en Java 8):Nota: La evaluación es perezosa ya que está pasando
java.util.function.Supplier
lambda que se evalúa solo cuandoThreadLocal#get
se llama pero el valor no se evaluó previamente.fuente
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.
fuente
ThreadLocal
patrón. Si lo haceF(){ member=random(); F2(); write(member); }
y F2 anula al miembro con un nuevo valor, entonces obviamentewrite(member)
ya no escribirá el número que harandom()
editado. Esto es literalmente solo sentido común. Del mismo modo, si lo haceF(){ F(); }
, ¡ tenga suerte con su bucle infinito! Esto es cierto en todas partes y no es específico paraThreadLocal
.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.
fuente
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
ThreadLocal
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 deThreadLocal
, pero lo ideé con fines ilustrativos.Dicho esto, tenga en cuenta que los
ThreadLocal
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.fuente
ThreadLocal
como campo de objeto ...ThreadLocal
, donde era accesible a nivel mundial. Como dijiste, todavía se puede usar como un campo de clase que limita la visibilidad.No hay nada realmente nuevo aquí, pero descubrí hoy que
ThreadLocal
es 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 usanLocale.getDefault()
. Puede configurar elValidator
con un diferenteMessageInterpolator
, pero no hay forma de especificarLocale
cuándo llamarvalidate
. Por lo tanto, puede crear un archivo estáticoThreadLocal<Locale>
(o mejor aún, un contenedor general con otras cosas que podría necesitar serThreadLocal
y luego hacer que suMessageInterpolator
selección personalizadaLocale
de eso. El siguiente paso es escribir unServletFilter
que use un valor de sesión orequest.getLocale()
elegir la configuración regional y almacenar en tuThreadLocal
referencia.fuente
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.
fuente
Por definición:
Cada uno
Thread
en Java contieneThreadLocalMap
en él.Dónde
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.Ahora
wrapper.getDateFormatter()
llamaráthreadlocal.get()
y eso comprobará quecurrentThread.threadLocalMap
contiene 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
fuente
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.
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.
fuente
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
fuente
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)
fuente
[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.
fuente
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.
fuente
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í
fuente
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.
fuente
Pruebe este pequeño ejemplo para tener una idea de la variable ThreadLocal:
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'.
fuente