Giriş
Şu satırı dahil ederiz
import java.lang.ThreadLocal;
Sınıf her zaman static final olarak tanımlamakta fayda var. Şöyle yaparız.
private static final ThreadLocal<Foo> fooHolder = new ThreadLocal<Foo>() {...}
Açıklaması şöyle.
ThreadLocal allows you to associate a per-thread value with a value-holding object. It allows you to store different objects for different threads and maintains which object corresponds to which thread. It has set and get accessor methods which maintain a separate copy of the value for each thread that uses it. The get() method always returns the most updated value passed to set() from the currently executing thread.
Sonar Uyarısı
Açıklaması şöyle
"ThreadLocal" variables should be cleaned up when no longer usedThreadLocal variables are supposed to be garbage collected once the holding thread is no longer alive. Memory leaks can occur when holding threads are re-used which is the case on application servers using pool of threads.To avoid such problems, it is recommended to always clean up ThreadLocal variables using the remove() method to remove the current thread’s value for the ThreadLocal variable.In addition, calling set(null) to remove the value might keep the reference to this pointer in the map, which can cause memory leak in some scenarios. Using remove is safer to avoid this issue.
Aslında bu problem daha çok Application Server kullanırken ortaya çıkıyor. Çünkü Application Server thread'leri ölmüyor. Dolayısıyla bir deployment unload edilse bile bellekten silinmiyor. Açıklaması şöyle
ThreadLocals are supposed to be garbage collected once the holding thread is no longer alive. But the problem arises when we use ThreadLocals along with modern application servers.
Modern application servers use a pool of threads to process requests, instead of creating new ones (for example, the Executor in the case of Apache Tomcat). Moreover, they also use a separate classloader.
Since Thread Pools in application servers work on the concept of thread reuse, they're never garbage collected; instead, they're reused to serve another request.
If any class creates a ThreadLocal variable, but doesn't explicitly remove it, then a copy of that object will remain with the worker Thread even after the web application is stopped, thus preventing the object from being garbage collected.
Örnek
Sonar kuralna göre ThreadLocal ile işimiz bitince remove() çağrısı yapmayan kod şöyle
// Noncompliant Code Example
public class ThreadLocalUserSession implements UserSession {
private static final ThreadLocal<UserSession> DELEGATE = new ThreadLocal<>();
public UserSession get() {
UserSession session = DELEGATE.get();
if (session != null) {
return session;
}
throw new UnauthorizedException("User is not authenticated");
}
public void set(UserSession session) {
DELEGATE.set(session);
}
public void incorrectCleanup() {
DELEGATE.set(null); // Noncompliant
}
// some other methods without a call to DELEGATE.remove()
}
Önerilen kod şöyle
// Compliant Solution
public class ThreadLocalUserSession implements UserSession {
private static final ThreadLocal<UserSession> DELEGATE = new ThreadLocal<>();
public UserSession get() {
UserSession session = DELEGATE.get();
if (session != null) {
return session;
}
throw new UnauthorizedException("User is not authenticated");
}
public void set(UserSession session) {
DELEGATE.set(session);
}
public void unload() {
DELEGATE.remove(); // Compliant
}
// ...
}
Test - Global ThreadLocal nesnesi
Şöyle yaparız. 0-4 arası çıktı görebiliriz. Yani her thread global bir ThreadLocal nesnesi olsa bile kendi değerini görüyor.
ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int index = 0; index < 5; index++) {
final int value = index;
executorService.submit(() -> {
threadLocal.set(value);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
...
}
System.out.println(" = " + threadLocal.get());
});
}
withInitial() metodu tercih edilebilir. Bu metod protected. ThreadLocal nesnesine değer atamak için çağrılır.
Örnek
Şöyle yaparız
static final ThreadLocal<MyParser> PARSER = new ThreadLocal<MyParser>() {
@Override
protected MyParser initialValue() {
return new MyParser();
}
};
Örnek
ThreadLocal sınıfı SimpleDateFormat, DecimalFormat gibi thread-safe olmayan nesnelerle beraber sıkça kullanılır. Şöyle yaparız.
Şöyle yaparız.
// SimpleDateFormat is not thread-safe, so give one to each thread
static final ThreadLocal<SimpleDateFormat> f= new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue(){
return new SimpleDateFormat("yyyyMMdd HHmm");
}
};
Daha sonra bu nesne şöyle kullanılıyor.public String formatIt(Date date) {
return f.get().format(date);
}
get metodu
Açıklaması şöyle.
Bu metod public'tir. ThreadLocal nesnemiz static final olduğu için şöyle yaparız.
Returns the value in the current thread's copy of this thread-local variable.
void parse(String input) {
PARSER.get().parse(input);
}
remove metodu
withInitial metodu - SupplierThreadLocal nesnemiz static final olduğu için şöyle yaparız.
public static void unset() {
PARSER.remove();
}
Şöyle yaparız.
ThreadLocal<Map<K, V>> safeMap = ThreadLocal.withInitial(() -> {...});
Açıklaması şöyle. Yan remove() ile değeri silsek bile tekrar withInitial ile verile değer ile başlarız.This method will be invoked the first time a thread accesses the variable with the get() method, unless the thread previously invoked the set(T) method, in which case the initialValue method will not be invoked for the thread. Normally, this method is invoked at most once per thread, but it may be invoked again in case of subsequent invocations of remove() followed by get().
Hiç yorum yok:
Yorum Gönder