ThreadLocal理解

类别:java  阅读:533  发布时间:Sun Jan 07 22:33:59 CST 2018

    ThreadLocal 是什么?

    今天阅读了一下 ThreadLocal 的源代码,发现跟此前在网上看到的一些博客说得相差甚远。很多人把 ThreadLocal 看成一个 key为当前线程的 Map ,其实是错误的。翻看源码才发现,其实 ThreadLocal 就是一个普通的类,不存储 get set 方法传来的变量,实际的变量存放在Thread 的 ThreadLocalMap 中。ThreadLocalMap 是 ThreadLocal 的一个静态内部类。

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();
}

    看看 ThreadLocal 的 get 方法,该方法可以分为几个步骤:

  1. 获取当前线程实例 。

  2. 获取当前线程实例上的 ThreadLocalMap 对象。

  3. 如果 ThreadLocalMap 不为空,根据 当前 ThreadLocal 对象为 key ,从ThreadLocalMap 中取变量。如果 ThreadLocalMap 为空,调用 setInitialValue 方法。

private T setInitialValue() {
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
}

    setInitialValue 方法也分为几个步骤:

    1.调用 initialValue 方法 获取 value 值。

    2.获取当前线程的 ThreadLocalMap ,如果不为空,以当前 ThreadLocal 对象为 key,设置 value 值,否则创建一个ThreadLocalMap ,并以当前 ThreadLocal 对象为 key,设置 value 值。

    实际应用的时候,ThreadLocal 实例和其他普通对象一样,会被拷贝到线程栈中,然后作为ThreadLocalMap 的 key 来使用。

-----------------------------------------------------------------------------------------------

    最近阅读到一段 jfinal 中 redis 相关的缓存插件代码。看到有用到 ThreadLocal。

public class RedisInterceptor implements Interceptor {
   protected Cache getCache() {
      return Redis.use();
   }
   
   public void intercept(Invocation inv) {
      Cache cache = getCache();
      Jedis jedis = cache.getThreadLocalJedis();
      if (jedis != null) {
         inv.invoke();
         return ;
      }
      
      try {
         jedis = cache.jedisPool.getResource();
         cache.setThreadLocalJedis(jedis);
         inv.invoke();
      }
      finally {
         cache.removeThreadLocalJedis();
         jedis.close();
      }
   }
}

    上面是 RedisInterceptor 的源码,作者的注释是:“RedisInterceptor 用于在同一线程中共享同一个 jedis 对象,提升性能.”。再看看 Cache 的部分代码。

public class Cache {
    protected final ThreadLocal threadLocalJedis = new ThreadLocal();
    
    public  T get(Object key) {
        Jedis jedis = getJedis();
        try {
            return (T) valueFromBytes(jedis.get(keyToBytes(key)));
        } finally {
            close(jedis);
        }
    }
    
    public Jedis getJedis() {
        Jedis jedis = threadLocalJedis.get();
        return jedis != null ? jedis : jedisPool.getResource();
    }
    
    public Jedis getThreadLocalJedis() {
        return threadLocalJedis.get();
    }
    
    public void setThreadLocalJedis(Jedis jedis) {
            threadLocalJedis.set(jedis);
        }
}

    可以看到 Cache 中放置了一个 ThreadLocal 变量,RedisInterceptor 初次获取到的 jedis 实例会调用 Cache 的 setThreadLocalJedis() 将 jedis 存放到 当前线程的 ThreadLocalMap 中去,而在实际业务代码中,会通过 Cache 的 getJedis() 来获取 jedis变量,而 getJedis() 会 从当前线程的 ThreadLocalMap 中取。这样就实现了“同一线程中共享同一个 jedis 对象”。

关键字:ThreadLocal

© copyright 粤ICP备16108162号-1