首页 > 技术文章 > java中ThrealLocal的理解

ElliottX4 2020-08-07 21:02 原文

java中threadlocal的理解

一、threadlocal的生命周期和ThreadLocalMap的生命周期

可以吧TreadLocal看做是一个map来使用,只不过这个map是指向当前线程中的threadLocals(ThreadLocalMap.class),这个threadLocals采用懒汉单例在一个线程中是唯一的。
Thread中的threadLocals属性,存放的是当前线程在不同TreadLocal实例中的值ThreadLocalMap<TreadLocal,T>

 public void set(T value) {
      Thread t = Thread.currentThread();
      ThreadLocalMap map = getMap(t);//return t.threadLocals;这里表示只有一个ThreadLocalMap副本
      if (map != null)//采用懒汉模式
          map.set(this, value);
      else
          createMap(t, value);//t.threadLocals = new ThreadLocalMap(this, firstValue);
  }

可以通过查看当前线程中的threadLocals变量,来看当前线程持有多少个threadLocal,以及存的值。

ThreadLocalMap实例的生命周期随着线程的结束而结束,因为ThreadLocalMap实例的唯一引用只存在当前线程中。ThreadLocal的生命周期,需要gc来决定,因为他的引用可能存在于多个线程中,此引用为弱引用。

弱引用的存在,导致可能内存泄露。当threadlocal=null,没有任何强引用实例指向threadLocals中的key本来指向的threadlocal实例,gc回收threadlocal,导致val永远不会被释放。采用以下代码来取代=null的操作,用remove函数来清除,threadLocals。

ThreadLocal local1 = new ThreadLocal();
local.remove();

二、ThreadLocal的作用

对于那些需要数据隔离,可以用ThreadLocal。

对于一个线程中的一个ThreadLocal只能存一个T类型的数据。(T为泛型)

ThreadLocal是线程安全的,所以可以用来封装线程不安全的实例,不同线程之间新创建实例,保证线程安全。(另一种模式就是单例模式。)

三、threadlocal示例

  1. 比较经典的例子就是github中的PageHelper中保存Page信息的时候使用了ThreadLocal。考虑到数据的同步,已经一个线程只会顺序执行sql语句。
 protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal();
 protected static void setLocalPage(Page page) {
         LOCAL_PAGE.set(page);
 }
  1. AopContext
    在同一个类中调用方法,导致aop不生效。原因是aop生效是调用的代理类,直接调用被代理类会无法触发切面。
@EnableAspectJAutoProxy(exposeProxy = true)//打开代理配置Appcontext

AopContext.currentProxy() //获取到当前类的代理类,可以进行强制转换



public abstract class AopContext {
    private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal("Current AOP proxy");//本质还是通过threadlocal实现
    public AopContext() {
    }
	public static Object currentProxy() throws IllegalStateException {
		Object proxy = currentProxy.get();
		if (proxy == null) {
			throw new IllegalStateException("Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.");
		} else {
			return proxy;
		}
	}

    static Object setCurrentProxy(Object proxy) {
        Object old = currentProxy.get();
        if (proxy != null) {
            currentProxy.set(proxy);
        } else {
            currentProxy.remove();
        }

        return old;
    }
}

四、InheritableThreadLocal的扩展

  1. InheritableThreadLocal extends ThreadLocal
  2. 子线程可以使用InheritableThreadLocal可以拿到父亲线程的变量,父线程无法拿到子线程的
  3. 子线程对变量的修改,父线程不可见。子线程修改的父线程那边拷贝的副本
  4. init子线程的时候,对threadlocal中的值进行复制
class Thread{
private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) {
......略
        Thread parent = currentThread();
        if (parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
......略
    }
}
  1. 和threallocal相比,只有map的引用不一样。

推荐阅读