¿Cómo se implementa ThreadLocal? ¿Está implementado en Java (usando algún mapa concurrente de ThreadID a Object), o usa algún enlace de JVM para hacerlo de manera más eficiente?¿Cómo se implementa el ThreadLocal de Java bajo el capó?
Respuesta
Todas las respuestas aquí son correctas, pero un poco decepcionantes ya que de alguna manera no se sabe cuán inteligente es la implementación de ThreadLocal
. Solo estaba mirando el source code for ThreadLocal
y quedé gratamente impresionado con la forma en que se implementó.
la implementación Naive
Si te pidiera que implementar una clase ThreadLocal<T>
dada la API se describe en el Javadoc, ¿qué harías? Una implementación inicial probablemente sería ConcurrentHashMap<Thread,T>
usando Thread.currentThread()
como su clave. Esto funcionaría razonablemente bien, pero tiene algunas desventajas.
- hilo de contención -
ConcurrentHashMap
es una clase muy inteligente, pero en última instancia, todavía tiene que lidiar con la prevención de múltiples hilos de ensuciar con él de ninguna manera, y si diferentes hilos golpean regularmente, habrá retrasos. - Permanentemente mantiene un puntero tanto al hilo como al objeto, incluso después de que el hilo haya terminado y pueda ser sometido a un GC.
La GC-amigable Implementación
intentarlo de nuevo Ok, vamos a tratar el tema de recolección de basura mediante el uso de weak references. Tratar con WeakReferences puede ser confuso, pero debería ser suficiente para utilizar un mapa construido de esta manera: (! Y que debe ser)
Collections.synchronizedMap(new WeakHashMap<Thread, T>())
O si estamos usando Guava:
new MapMaker().weakKeys().makeMap()
Este significa que una vez que nadie más se aferra al hilo (lo que implica que ha terminado) la clave/valor puede ser basura, lo cual es una mejora, pero aún no soluciona el problema de contención de hilos, lo que significa que hasta ahora nuestro ThreadLocal
no es todo increíble de una clase. Además, si alguien decidiera retener los objetos Thread
después de que hubieran terminado, nunca recibirían GC, y por lo tanto tampoco nuestros objetos, aunque ahora son técnicamente inalcanzables.
la implementación inteligente
Hemos estado pensando acerca ThreadLocal
como una asignación de temas a los valores, pero tal vez eso no es realmente la manera correcta de pensar en ello. En lugar de pensar en ello como un mapeo de Threads a valores en cada objeto ThreadLocal, ¿qué pasaría si lo consideráramos una asignación de objetos ThreadLocal a los valores en cada Thread?Si cada hilo almacena la asignación, y ThreadLocal simplemente proporciona una interfaz agradable en esa asignación, podemos evitar todos los problemas de las implementaciones anteriores.
Una implementación sería algo como esto:
// called for each thread, and updated by the ThreadLocal instance
new WeakHashMap<ThreadLocal,T>()
No hay necesidad de preocuparse por la concurrencia aquí, porque sólo un hilo siempre tendrá acceso a este mapa.
Los desarrolladores Java tienen una gran ventaja sobre nosotros aquí: pueden desarrollar directamente la clase Thread y agregarle campos y operaciones, y eso es exactamente lo que han hecho.
En java.lang.Thread
hay las siguientes líneas:
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null;
que como su comentario sugiere es de hecho un mapeo paquete-privada de todos los valores de ser rastreado por ThreadLocal
objetos para este Thread
. La implementación de ThreadLocalMap
no es WeakHashMap
, pero sigue el mismo contrato básico, que incluye mantener sus claves por referencia débil.
ThreadLocal.get()
se implementa a continuación de este modo:
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
Y ThreadLocal.setInitialValue()
así:
private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; }
Esencialmente, utilice un mapa en este Tema para mantener todas nuestros objetos ThreadLocal
. De esta forma, nunca tenemos que preocuparnos por los valores en otros subprocesos (ThreadLocal
, literalmente, solo pueden acceder a los valores en el subproceso actual) y, por lo tanto, no tienen problemas de concurrencia. Además, una vez que se completa el Thread
, su mapa se someterá automáticamente a GC y se limpiarán todos los objetos locales. Incluso si el Thread
se mantiene, los objetos ThreadLocal
se mantienen por referencia débil, y se pueden limpiar tan pronto como el objeto ThreadLocal
quede fuera del alcance.
Ni que decir tiene, que estaba bastante impresionado por esta aplicación, es bastante elegante recibe alrededor de un montón de problemas de concurrencia (la verdad, tomando ventaja de ser parte del núcleo de Java, pero eso es perdonable ellos ya que es una ingeniosa tales clase) y permite un acceso rápido y seguro a subprocesos a objetos a los que solo se debe acceder mediante un subproceso a la vez.
tl; drThreadLocal
la implementación es bastante buena, y mucho más rápida/más inteligente de lo que piensas a primera vista.
Si te gustó esta respuesta, puedes ver que me gusta (menos información) discussion of ThreadLocalRandom
. fragmentos
Thread
/ThreadLocal
código tomado de Oracle/OpenJDK's implementation of Java 8.
Las variables de ThreadLocal en Java funcionan accediendo a un HashMap mantenido por la instancia Thread.currentThread().
Eso no es correcto (o al menos no lo es ya). Thread.currentThread() es una llamada nativa en Thread.class. También Thread tiene un "ThreadLocalMap" que es un solo grupo (matriz) de códigos hash. Este objeto no es compatible con la interfaz de Mapa. – user924272
Eso es básicamente lo que dije. currentThread() devuelve una instancia Thread, que contiene un mapa de ThreadLocals a valores. –
¡No es un mapa! – user924272
Quiere significar java.lang.ThreadLocal
. Es bastante simple, realmente, es solo un Mapa de pares de nombre-valor almacenados dentro de cada objeto Thread
(vea el campo Thread.threadLocals
). La API oculta ese detalle de implementación, pero eso es más o menos todo lo que hay que hacer.
Entonces, no incurre en bloqueos o contención, ¿verdad? – ripper234
No puedo ver por qué debería ser necesario, dado que, por definición, los datos solo son visibles para un solo hilo. – skaffman
Correcto, no hay sincronización ni bloqueo alrededor o dentro de ThreadLocalMap, ya que solo se accede in-thread. – Cowan
Supongamos que va a implementar ThreadLocal
, ¿cómo lo hace específico de subprocesos? Por supuesto, el método más simple es crear un campo no estático en la clase Thread, llamémoslo threadLocals
. Debido a que cada hilo está representado por una instancia de hilo, por lo que threadLocals
en cada hilo sería diferente, también. Y esto es también lo que hace de Java:
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
Lo que es ThreadLocal.ThreadLocalMap
aquí? Como solo tiene un threadLocals
para un hilo, entonces si simplemente toma threadLocals
como su ThreadLocal
(digamos, define threadLocals como Integer
), solo tendrá un ThreadLocal
para un hilo específico. ¿Qué sucede si quiere múltiples variables ThreadLocal
para un hilo? La forma más simple es hacer threadLocals
un HashMap
, el key
de cada entrada es el nombre de la variable ThreadLocal
, y el value
de cada entrada es el valor de la variable ThreadLocal
. Un poco confuso? Digamos que tenemos dos hilos, t1
y t2
. toman la misma instancia Runnable
como el parámetro del constructor Thread
, y ambos tienen dos variables ThreadLocal
llamadas tlA
y tlb
. Esto es lo que es.
t1.tlA
+-----+-------+
| Key | Value |
+-----+-------+
| tlA | 0 |
| tlB | 1 |
+-----+-------+
t2.tlB
+-----+-------+
| Key | Value |
+-----+-------+
| tlA | 2 |
| tlB | 3 |
+-----+-------+
en cuenta que los valores están compuestos por mí.
Ahora parece perfecto. ¿Pero qué es ThreadLocal.ThreadLocalMap
? ¿Por qué no solo usó HashMap
? Para resolver el problema, vamos a ver lo que sucede cuando fijamos un valor a través del método de la clase ThreadLocal
set(T value)
:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
getMap(t)
simplemente devuelve t.threadLocals
. Debido a t.threadLocals
se initilized null
, así que entramos createMap(t, value)
primera:
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
Se crea una nueva instancia utilizando ThreadLocalMap
la corriente ThreadLocal
instancia y el valor que se creará. Vamos a ver lo que es como ThreadLocalMap
, es de hecho parte de la clase ThreadLocal
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
...
/**
* Construct a new map initially containing (firstKey, firstValue).
* ThreadLocalMaps are constructed lazily, so we only create
* one when we have at least one entry to put in it.
*/
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
...
}
La parte central de la clase ThreadLocalMap
es la Entry class
, que se extiende WeakReference
. Asegura que si sale el hilo actual, se recolectará basura automáticamente. Es por eso que usa ThreadLocalMap
en lugar de un simple HashMap
.Se pasa la corriente ThreadLocal
y su valor como parámetro de la clase Entry
, por lo que cuando queremos obtener el valor, que podría obtener de table
, que es una instancia de la clase Entry
:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
Este es lo que es como en todo el cuadro:
conceptualmente, se puede pensar en un ThreadLocal<T>
como la celebración de un Map<Thread,T>
que almacena los valores de rosca específico, aunque esto no es lo que es en realidad implementado.
Los valores específicos de hilo se almacenan en el objeto Thread mismo; cuando el hilo termina, los valores específicos del hilo pueden ser recogidos.
Referencia: JCIP
Conceptalmente, sí. Pero como ve en otras respuestas anteriores, la implementación es completamente diferente. – Archit
- 1. Herencia bajo el capó
- 2. ¿Cómo se implementa la programación basada en eventos de Win32 bajo el capó?
- 3. ¿Cómo funciona gmail bajo el capó?
- 4. ¿Cómo funciona el método .delay de jQuery bajo el capó?
- 5. ¿Cómo funcionan las animaciones de Android bajo el capó?
- 6. ¿Cómo implementa Java el patrón de peso mosca para la cuerda debajo del capó?
- 7. Http and file uploading - ¿Cómo funciona realmente (bajo el capó)
- 8. Rails 3 ActiveRecord método de encadenamiento, bajo el capó
- 9. ¿Cómo funciona la frase de contraseña de clave privada de RSA bajo el capó?
- 10. Entender EF bajo el capó. Incluir vs Uniones
- 11. ¿Cómo se impiden las palabras clave 'from/where/select' bajo el capó en LINQ y C#?
- 12. ¿Cómo se implementan los tipos anulables bajo el capó en .net?
- 13. ¿Cómo se implementa el método String # intern() de Java?
- 14. ¿Cómo se implementa Super en Java?
- 15. ¿Cómo se implementa el resaltado de sintaxis?
- 16. ¿Cómo se implementa "Sitio bajo mantenimiento" para el sitio web de Windows Azure?
- 17. ¿Qué hace Notepad diferente bajo el capó que el comando PRINT?
- 18. ¿Cómo funciona FormView EditTemplate Actualizar valores en ObjectDataSource UpdateParameters bajo el capó?
- 19. ¿Cómo funcionan las transacciones bancarias "bajo el capó", posiblemente en detalle
- 20. ¿Cómo se implementa el Alcance léxico?
- 21. ¿Cómo se implementa el método http post?
- 22. ¿Cómo se implementa DependencyObject de WPF?
- 23. ¿Cómo se implementa el manejo de excepciones por los lenguajes de programación?
- 24. Usos de ThreadLocal
- 25. De vuelta en los fundamentos de OO. ¿Cómo funciona LinkedHashMap bajo el capó? No puedo entender esto
- 26. ¿Cómo funciona una Llamada bajo el capó? ¿Cómo es posible que un objeto invocable devuelva un valor?
- 27. ¿cómo se implementa sarcmark?
- 28. ¿Cómo se implementa "const"?
- 29. ¿Cómo se implementa OpenID?
- 30. Java - ¿Cómo verificar el valor de las variables 'ThreadLocal' en Eclipse?
Tus respuestas se ven increíbles, pero ahora es demasiado tiempo para mí. +1 y aceptado, y lo estoy agregando a mi cuenta de getpocket.com para leerlo más tarde. ¡Gracias! – ripper234
Necesito una cosa similar a ThreadLocal que también me permita acceder a la lista completa de valores, al igual que map.values (). Entonces, mi implementación ingenua es WeakHashMap donde la clave es Thread.currentThread(). GetName(). Esto evita la referencia al hilo en sí. Si el hilo se va, entonces nada retiene el nombre del hilo (una suposición, lo admito) y mi valor desaparecerá. –
bmauter
En realidad, [respondí una pregunta a tal efecto recientemente] (http://stackoverflow.com/a/15654081/113632). Un 'WeakHashMap' presenta varios problemas, no es seguro para el hilo, y "está destinado principalmente para usar objetos claves cuyos métodos iguales prueban la identidad del objeto usando el operador ==", así que usar el objeto 'Thread' como una clave hacerlo mejor. Sugiero usar el mapa de Keys débiles de Guava que describí arriba para su caso de uso. –
dimo414