2009-06-02 6 views
10

La mia comprensione limitata di ThreadLocal è che ha resource leak issues. Ho capito che questo problema può essere risolto con l'uso corretto di WeakReferences con ThreadLocal (sebbene possa aver frainteso questo punto). Vorrei semplicemente un modello o un esempio per utilizzare correttamente ThreadLocal con WeakReference, se ne esiste uno. Per esempio, in questo frammento di codice dove verrà introdotto il WeakReference?ThreadLocal Resource Leak and WeakReference

static class DateTimeFormatter { 
    private static final ThreadLocal<SimpleDateFormat> DATE_PARSER_THREAD_LOCAL = new ThreadLocal<SimpleDateFormat>() { 
     protected SimpleDateFormat initialValue() { 
      return new SimpleDateFormat("yyyy/MM/dd HH:mmz"); 
     } 
    }; 
    public String format(final Date date) { 
     return DATE_PARSER_THREAD_LOCAL.get().format(date); 
    } 
    public Date parse(final String date) throws ParseException 
    { 
     return DATE_PARSER_THREAD_LOCAL.get().parse(date); 
    } 
} 
+1

Perché credo che abbia problemi di risorse? Il motivo per cui lo chiedo è perché nella mia esperienza, non ho avuto un problema. –

+0

Si prega di fornire alcune informazioni sul motivo per cui si ritiene che ci sia una perdita di memoria. – erickson

+0

Aggiornato. Si prega di consultare il link "Problemi di perdita di risorse" sopra. –

risposta

27

ThreadLocal utilizza un WeakReference internamente.Se lo ThreadLocal non è fortemente referenziato, verrà raccolto in modo improprio, anche se vari thread hanno valori memorizzati tramite tale ThreadLocal.

Inoltre, i valori ThreadLocal vengono effettivamente memorizzati nello Thread; se un thread muore, vengono raccolti tutti i valori associati a quel thread tramite ThreadLocal.

Se si dispone di un ThreadLocal come membro della classe finale, si tratta di un riferimento sicuro e non può essere raccolto fino a quando la classe non viene scaricata. Ma questo è come funziona qualsiasi membro della classe, e non è considerato una perdita di memoria.


Update: Il problema citato entra in gioco solo quando il valore memorizzato in un ThreadLocal riferimento fortemente che ThreadLocal — sorta di un riferimento circolare.

In questo caso, il valore (a) non ha alcun riferimento all'indietro allo ThreadLocal. Non c'è perdita di memoria in questo codice.

+0

Perdita quando un valore locale del thread (indirettamente) fa riferimento a ThreadLocal è ancora un bug nell'implementaiton di Sun. Non sono sicuro che l'implementazione pazzesca di Bob Lee in Harmony abbia lo stesso problema. –

+0

Si tratta di un bug in ThreadLocal o dell'applicazione e del suo uso di ThreadLocal? Non ci ho ancora provato (e cerco di evitare ThreadLocal), quindi non sono ancora sicuro. – erickson

+0

Nota che in un ambiente Servlet hai bisogno di usare un filtro servlet per cancellare quel ThreadLocal, se non lo fai avrai molte perdite su redeploys –

3

Non non dovrebbe essere un problema.

Il riferimento ThreadLocal di un thread è definito per esistere solo finché il thread corrispondente è vivo (si veda javadoc) - o in un altro modo, una volta che il thread non è attivo, se ThreadLocal era l'unico riferimento a quell'oggetto , quindi l'oggetto diventa idoneo per la garbage collection.

Quindi, hai trovato un bug reale e dovresti segnalarlo o stai facendo qualcos'altro di sbagliato!

+0

Considera il caso in cui stai eseguendo all'interno di un server/contenitore di app che può scaricare l'applicazione (per installare, ad esempio, una versione diversa) ma continuare a utilizzare i thread per una nuova versione dell'app. –

1

Solo per aggiungere ciò che ha detto @Neil Coffey, questo non dovrebbe costituire un problema finché l'istanza ThreadLocal è statica. Poiché continui a chiamare get() su un'istanza statica, dovrebbe sempre contenere lo stesso riferimento al tuo semplice formattatore di date. Pertanto, come ha detto Neil, al termine del Thread, l'unica istanza di formattatore di date semplice dovrebbe essere idonea per la garbage collection.

Se si dispone di numeri o qualche altra forma di debug che mostra questo problema di introduzione, allora questa è un'altra storia. Ma credo che non dovrebbe essere un problema.

10

Sto immaginando che stai saltando attraverso questi cerchi poiché SimpleDateFormat è non thread-safe.

Sebbene sia a conoscenza che non risolvo il problema sopra riportato, posso suggerire di guardare Joda per la data/ora di lavoro? Joda ha un meccanismo di formattazione data/ora sicuro per i thread. Non perderai tempo a imparare l'API Joda, poiché è la base per la nuova proposta di data/ora standard per le API.

+1

+1: non ci avevo nemmeno pensato. È una buona scommessa che questo è il caso, se non per questo OP, quindi forse per qualcun altro. –

0

Nel tuo esempio non dovrebbe esserci alcun problema nell'utilizzare un ThreadLocal.

I locali di thread (e anche i singleton!) Diventano un problema quando i locali dei thread sono impostati su istanze caricate da un classloader da scaricare in un secondo momento. Una situazione tipica in un contenitore servlet (come tomcat):

  1. Una webapp imposta un thread locale durante l'elaborazione della richiesta.
  2. Il thread è gestito dal contenitore.
  3. La webapp deve essere annullata.
  4. Il classloader dell'app web non può essere garbato, perché è rimasto un riferimento: dal thread locale all'istanza alla sua classe al suo classloader.

Lo stesso vale per single (driver java.sql.DriverManger e JDBC offerti dalla webapp).

Quindi evitare tali cose in particolare in ambienti non sotto il pieno controllo!

+3

No, i formattatori non sono thread-safe. Più thread che invocano il metodo di analisi di una singola istanza sono un disastro. – erickson

+0

Buoni punti sulle insidie ​​di ThreadLocals però! – erickson

+0

ok, non ha pensato a SimpleDateFormat, ma non ci dovrebbero essere problemi con ThreadLocal comunque. –

2

Mi rendo conto che questa non è una risposta rigorosa alla tua domanda, ma come regola generale non suggerirò di utilizzare uno ThreadLocal in situration in cui non vi è una chiara lacerazione alla fine di una richiesta/interazione. Il classico sta facendo questo genere di cose in un contenitore servlet, a prima vista sembra soddisfacente, ma dal momento che i thread sono raggruppati diventa un problema con il ThreadLocal appeso alla risorsa anche dopo che ogni richiesta è stata elaborata.

Suggerimenti:

  1. utilizzare un filtro di involucro simile per ogni interazione per cancellare il ThreadLocal alla fine di ogni interazione
  2. È possibile utilizzare un alternativa al SimpleDateFormat come FastDateFormat from commons-lang o Joda come qualcuno ha già suggerito
  3. Basta creare un nuovo SimpleDateFormat ogni volta che ne hai bisogno. Sembra uno spreco Lo so, ma nella maggior parte delle applicazioni che hai appena non si noterà la differenza
0

pulito il filo locale dopo l'uso, aggiungere il filtro servlet per farlo:

 
protected ThreadLocal myThreadLocal = new ThreadLocal(); 

public void doFilter (ServletRequest req, ServletResponse res, chain) throws IOException, ServletException { 
    try { 
     // Set ThreadLocal (eg. to store heavy computation result done only once per request) 
     myThreadLocal.set(context()); 

     chain.doFilter(req, res); 
    } finally { 
     // Important : cleanup ThreaLocal to prevent memory leak 
     userIsSmartTL.remove(); 
    } 
}