java基础:ThreadLocal

185 阅读2分钟

作用

ThreadLocal不解决线程间共享变量的问题,反而解决的是,各个线程独有变量的问题。这样这个变量不用再考虑线程安全的问题。因为每个线程就有这个变量的一个副本,而这个变量副本由于只有一个线程在访问,因此不用考虑线程安全性。

可以考虑下netty的线程模型,每个channel都被一个线程访问,因此总是线程安全的。

实现

每个线程一个变量的副本。每个线程维护一个ThreadLocal变量到其持有的具体实例的映射。

内存泄漏

上面讲到的映射,其实ThreadLocal是个弱引用,也就是当没有这个ThreadLocal的强引用时,这个ThreadLocal就会被回收。

弱引用:不管内存充足不充足都会被GC。

软引用:只有当内存不充足时才会被GC。

但是,ThreadLocal持有的具体实例却是个强引用。


/**
 * 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;
    }
}

由于ThreadLocal被GC了,但是该Entry的value还有引用,无法被GC。

怎么办呢?

ThreadLocal的API get、set操作,当遍历到key为null的情况时,会将该Entry的value置为null。

// ThreadLocal.get()
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();
}

// map.getEntry(this)
private Entry getEntry(ThreadLocal<?> key) {
    int i = key.threadLocalHashCode & (table.length - 1);
    Entry e = table[i];
    if (e != null && e.get() == key)
        return e;
    else
        return getEntryAfterMiss(key, i, e);
}

// getEntryAfterMiss
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
    Entry[] tab = table;
    int len = tab.length;

    while (e != null) {
        ThreadLocal<?> k = e.get();
        if (k == key)
            return e;
        // 如果为null就会移除
        if (k == null)
            expungeStaleEntry(i);
        else
            i = nextIndex(i, len);
        e = tab[i];
    }
    return null;
}

参考