How to correctly handle ThreadLocal.get() returning null

Java's ThreadLocals make certain things easy, but special care must be taken to make sure they are removed from threads when they are no longer needed. If ThreadLocals are not removed from threads, they can easily cause memory leaks, because threads can live much longer than the applications that are executed on a thread.

Consider a web application running inside a servlet container like Tomcat. Each request is handled by a thread from a thread pool. These threads can live well after you web app is undeployed from the server.

One of the easiest ways to get this wrong is to assume that ThreadLocal.get() returning null means that the ThreadLocal is not installed in the thread. This is not the case.

Each thread has a map of ThreadLocals, and when ThreadLocal.get() is called for the first time, it actually installs the ThreadLocalinto that ThreadLocal map for the current thread with the default value, which is usually null. You must still remove the ThreadLocal from the thread by calling ThreadLocal.remove().

As a rule of thumb, for ThreadLocals where the default value is null, if get() returns nullremove() should be called immediately.

I have written a class that does most of this for me:

/**
 * A conservative {@link ThreadLocal} that removes itself from the thread
 * whenever its value is {@code null}.
 * @author Jesse Long
 * @param <T> The type of the value stored in this {@link ThreadLocal}
 */
public abstract class ConservativeThreadLocal<T>
        extends ThreadLocal<T>
{
    /**
     * Returns the current value of the variable, or {@code null} if it has not
     * been set. This method takes care to remove this {@link ThreadLocal} from
     * the thread if the value has not been set.
     * @return the current value of the variable, or {@code null} if it has not
     * been set.
     */
    @Override
    public final T get()
    {
        T t = super.get();
        if (t == null){
            remove();
        }
        return t;
    }

    /**
     * Returns {@code null}. This implementation always returns {@code null} so
     * that this {@link ThreadLocal} implementation can use its absent on a
     * thread as an indication that the value is not set.
     * @return {@code null}
     */
    @Override
    protected final T initialValue()
    {
        return null;
    }

    /**
     * Returns the current value of the variable. If the value has not been
     * set, and {@code create} is {@code true}, then the {@link #create()}
     * method is called, the value of the {@link ThreadLocal} is set to the
     * return value of {@link #create()}, and is returned from this method. If
     * the value has not been set, and {@code create} is {@code false}, then
     * this method behaves exactly the same as the {@link #get()} method.
     * 
     * @return the current value of the variable, or the default value if
     * {@code create} is {@code true}.
     * 
     * @param create whether or not to set the default value if it has not yet
     * been set.
     */
    public T get(boolean create)
    {
        T t = get();
        if (t == null && create){
            t = create();
            set(t);
        }
        return t;
    }

    /**
     * Sets the current value. If {@code value} is {@code null}, then this
     * {@link ThreadLocal} is removed from the thread.
     * @param value The value to set.
     */
    @Override
    public void set(T value)
    {
        if (value == null){
            remove();
        }else{
            super.set(value);
        }
    }

    /**
     * Returns the default value for this object.
     * @return the default value for this object.
     * @see #get(boolean) 
     */
    protected abstract T create();
}
posted @   邱明成  阅读(543)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
点击右上角即可分享
微信分享提示