学习ThreadLocal实现原理

ThreadLocal的思想是以空间换时间,而锁的思想是以时间换空间,threadLocal的实现就是讲成员变量的副本拷贝到自己的线程内部,

因为不同的线程之间是隔离的,所以可以通过这种方式实现线程安全。

先来看一个示例:

/**
 * 分析ThreadLocal类的实现原理
 * 我们解决线程安全问题,那么以时间换空间,使用锁机制。要么以空间
 * 换时间,使用threadLocal,隔离线程
 */
public class AnalysisThreadLocal {

    private static ThreadLocal<Map<String, String>> threadLocal = new ThreadLocal<Map<String, String>>() {
        @Override protected Map<String, String> initialValue() {
            return new HashMap<>();
        }
    };

    public static void main(String[] args) throws ParseException {
        new Thread(() -> {
            threadLocal.get().put("1", "aaaaa");
            String key = threadLocal.get().get("1");
            System.out.println(Thread.currentThread().getName() + " key: " + key);
        }).start();

        new Thread(() -> {
            String key = threadLocal.get().get("1");
            System.out.println(Thread.currentThread().getName() + " key: " + key);
        }).start();

    }
}

  

执行结果:

 线程Thread-0 在自己的副本中put值,然后 在线程Thread-1获取不到。

get方法:

/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
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();
}

看一下注释:这个方法会返回当前线程的thread-local变量副本,如果当前线程没有副本值,则首先

调用initialValue方法来初始化这个副本值。

 

首先到Thread类的成员变量上去取副本值,ThreadLocalMap map = getMap(t);

 如果为null则调用初始化方法:

T value = initialValue();
这个方法会调到我们自己的实现类方法,实例化具体需要保证线程安全的变量。

然后将当前ThreadLocal对象与变量维护到ThreadLocalMap中,然后赋值给当前线程的字段ThreadLocals

再看一下ThreadLocalMap这个类,传进来的ThreadLocal对象与变量维护到Entry节点上,

 

完成以上设置,将ThreadLocal与变量封装成一个ThreadLocalMap,然后赋值给Thread的字段后,就把这个变量返回了。

然后我们可以使用这个变量,示例中我们的变量是一个HashMap,我们向里面put值。

然后在当前线程再get出来。

这时候getMap就可以拿到值,ThreadLocalMap

 调用map.getEntry 拿到Entry值,然后在拿到变量的值HashMap,然后就可以根据key找到value值。

如果这时候另外一个线程也想拿到这个值,

然后这个线程为重复第一个线程的动作,也会调用初始化方法,拷贝一个副本,将ThreadLocal对象与副本封装成ThreadLocalMap,赋值给

当前线程的ThreadLocals字段,然后将变量返回,因为是新的HashMap,所以我们那第一个线程设置的key去取值,得到的一定是null。

ThreadLocal常见的问题

1:内存泄漏,内存泄漏就是因为一些因素导致对象不能被回收,长期的占用内存,比如内存本来容量是10M,一直被一些长期的不可回收的对象

占用2M,那么实际容量其实只有8M

因为这里的Entry实现了弱引用父类,所以,如果发生gc,而且这些ThreadLocal对象没有外部强引用的话,就会被回收掉。

 那么entry中的key就会为null,如果key为null,则value不再会被remove,所以这些value不会被释放。

 

猜你喜欢

转载自www.cnblogs.com/warrior4236/p/12507210.html