本地线程变量(四):FastThreadLocal

FastThreadLocal

一、背景

​ 因为需要,研究了可以通过InheritableThreadLocal进行父子线程中如何传递本地线程变量,通过阿里开源项目TransmitableThreadLocal进行进行线程池传递本地线程变量(详解可查看以往博客)。在查找资料的过程中无意发现了DobboInternalThreadLocal,其实DobboInternalThreadLocalnettyFastThreadLocal有异曲同工之妙。之前学netty的时候有了解一点,为了加深一下ThreadLocal种群的了解,使用本博客记录一下。

二、实例
public static void main(String[] args) {
	
  /**
   * 原生线程调用方式
   */
  new Thread(new Runnable() {
    @Override
    public void run() {
      username.set("zhangSang");
      password.set("123456");
      password.remove();

      System.out.println(username.get());
      System.out.println(password.get());
    }
  }).start();
	
	/**
   * FastThreadLocalThread线程调用方式
   */
  new FastThreadLocalThread(new Runnable() {
    @Override
    public void run() {
      username.set("zhangSang");
      password.set("123456");

      System.out.println(username.get());
      System.out.println(password.get());
    }
  }).start();
}

单从运行结果看,都能获取到对应设置的值,二者没有任何输出区别,但是跟踪一下,可以看到调用的逻辑是有所区别的。

三、原理

​ 熟悉ThreadLocal的应该知道,Threadlocal其实内部逻辑是一个以ThreadLocal对象为key,需要存储的值为valuemap结构。而FastThreadLocal的内部实现是一个数组,实现上是直接通过下标定位元素,所以单纯从取、存的角度看,FastThreadLocal是比ThreadLocal高效。

​ 对于使用原生线程Thread来说,其实最后是将数据存Thread.threadLocals(ThreadLocal.ThreadLocalMap)中,也就是说在这个线程所使用的所有FastThreadLocal最后都以 key=ThreadLocal<InternalThreadLocalMap>
对象,value=InternalThreadLocalMap的方式存在线程ThreadLocal的一个节点中。而若使用的是netty封装的FastThreadLocalThread,则FastThreadLocalThread对象的属性threadLocalMap中。

FastThreadLocalThread是直接继承于线程类Thread,并且内部维护一个InternalThreadLocalMap,用于存储变量。虽然这个类命名为Map,结构上其实是一个数组。并且下标为0的元素是一个Set<FastThreadLocal<?>>的结构,存储着当前有效的FastThreadLocal

public class FastThreadLocalThread extends Thread{
  private InternalThreadLocalMap threadLocalMap;
}

InternalThreadLocalMap中提供了一些静态方法,通过当前线程的不同类型,以不同的方式获取对应所需要的InternalThreadlocalMap

/**
 * 获取方法
 */
public static InternalThreadLocalMap get() {
  Thread thread = Thread.currentThread();
  // 根据不同的线程类型,以不同的方式获取对应的InternalThreadLocalMap
  if (thread instanceof FastThreadLocalThread) {
    return fastGet((FastThreadLocalThread) thread);
  } else {
    return slowGet();
  }
}

/**
 * FastThreadLocalThred获取方法
 */
private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) {
  // 直接从FastThreadLocalThread对象中获取InternalThreadLocalMap
  InternalThreadLocalMap threadLocalMap = thread.threadLocalMap();
  // 若为空,初始化一个InternalThreadLocalMap
  if (threadLocalMap == null) {
    thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap());
  }
  return threadLocalMap;
}

/**
 * 原生线程的获取方法
 */
private static InternalThreadLocalMap slowGet() {
 /**
  * 从UnpaddedInternalThreadLocalMap中获取到ThreadLocal<InternalThreadLocalMap>
  * 在从ThreadLocal中获取InternalThreadLocalMap,若为空初始化一个。所以由此可知,原生线程
  * 的FasstThreadLocal具体值,是以InternalThreadLocalMap为值,存储在ThreadLocal中。
  */
  ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = 
    UnpaddedInternalThreadLocalMap.slowThreadLocalMap;
  InternalThreadLocalMap ret = slowThreadLocalMap.get();
  if (ret == null) {
    ret = new InternalThreadLocalMap();
    slowThreadLocalMap.set(ret);
  }
  return ret;
}
1.set值
public final void set(V value) {
  if (value != InternalThreadLocalMap.UNSET) {
    // 根据不同的线程类型,获取到的InternalThreadLocalMap进行设置值。
    set(InternalThreadLocalMap.get(), value);
  } else {
    remove();
  }
}

public final void set(InternalThreadLocalMap threadLocalMap, V value) {
  if (value != InternalThreadLocalMap.UNSET) {
    // 将需要存储的值添加到InternalThreadLocalMap对应的下标位置。
    if (threadLocalMap.setIndexedVariable(index, value)) {
      addToVariablesToRemove(threadLocalMap, this);
    }
  } else {
    remove(threadLocalMap);
  }
}

private static void addToVariablesToRemove(InternalThreadLocalMap threadLocalMap,
                                           FastThreadLocal<?> variable) {
  // 根据下标获取对应的元素
  Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);
  Set<FastThreadLocal<?>> variablesToRemove;
  if (v == InternalThreadLocalMap.UNSET || v == null) {
    // 若为空,创建set结构并将值添加到对应的下标位置
    variablesToRemove = Collections.newSetFromMap(new IdentityHashMap<FastThreadLocal<?>, Boolean>());
    threadLocalMap.setIndexedVariable(variablesToRemoveIndex, variablesToRemove);
  } else {
    // 否则直接应用取出的值
    variablesToRemove = (Set<FastThreadLocal<?>>) v;
  }
	// 添加元素到set集合中
  variablesToRemove.add(variable);
}
2.get值
public final V get() {
  return get(InternalThreadLocalMap.get());
}

 /**
  * get操作比较简单,直接从threadLocalMap获取对应的下标元素返回。
  */
public final V get(InternalThreadLocalMap threadLocalMap) {
  Object v = threadLocalMap.indexedVariable(index);
  if (v != InternalThreadLocalMap.UNSET) {
    return (V) v;
  }
  return initialize(threadLocalMap);
}

/**
  * 是一个默认值操作,可以通过最后的initValue()会调用FastThreadLocal#initialValue
  * 做一个初始化的操作。
  */
private V initialize(InternalThreadLocalMap threadLocalMap) {
  V v = null;
  try {
    v = initialValue();
  } catch (Exception e) {
    PlatformDependent.throwException(e);
  }

  threadLocalMap.setIndexedVariable(index, v);
  addToVariablesToRemove(threadLocalMap, this);
  return v;
}
3.remove
public final void remove() {
  remove(InternalThreadLocalMap.getIfSet());
}

@SuppressWarnings("unchecked")
public final void remove(InternalThreadLocalMap threadLocalMap) {
  if (threadLocalMap == null) {
    return;
  }
	// 删除对应下标值,赋值为UNSET占位符
  Object v = threadLocalMap.removeIndexedVariable(index);
  //删除InternalThreadLocalMap[0]的Set<FastThreadLocal<?>>中的当前FastThreadLocal对象
  removeFromVariablesToRemove(threadLocalMap, this);
  if (v != InternalThreadLocalMap.UNSET) {
    try {
      // 是一个增强,删除之后的业务逻辑,由子类实现,默认是空实现
      onRemoval((V) v);
    } catch (Exception e) {
      PlatformDependent.throwException(e);
    }
  }
}
四、总结

FastThreadLocal实际上采用的是数组的方式进行存储数据,在数据的获取、赋值都是通过下标的方式进行,而ThreadLocal是通过map结构,先计算哈希值,在进行线性探测的方式进行定位。所以在高并发下,FastThreadLocal应该相对高效,但是FastThread有一个弊端就是index是一直累加,也就是说如果移除了某个变量是通过将对应下标的元素标记为UNSET占位,而不进行回收,会无限制增大,会触发扩容等一些问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值