前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ThreadLocal与FastThreadLocal

ThreadLocal与FastThreadLocal

作者头像
书唐瑞
发布2022-06-02 13:56:49
1910
发布2022-06-02 13:56:49
举报
文章被收录于专栏:Netty历险记
代码语言:javascript
复制
java.lang.ThreadLocal

ThreadLocal类通过线程封闭的方式解决线程安全,提到它,大家都会想到弱引用和内存泄漏等话题,它的get/set/remove等方法,网上有很多关于它的话题和详细介绍.这篇文章不会介绍这些内容.

ThreadLocal作为key被存放到线程的ThreadLocal.ThreadLocalMap中.我们简单看下它的set方法.

代码语言:javascript
复制
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
      // this就是ThreadLocal,它作为key
        map.set(this, value);
    else
        createMap(t, value);
}
代码语言:javascript
复制
private void set(ThreadLocal<?> key, Object value) {

    Entry[] tab = table;
    int len = tab.length;
    // 这里的key就是ThreadLocal对象,根据它的threadLocalHashCode属性值进行取模计算,得到它应该所在的下标值是多少
    int i = key.threadLocalHashCode & (len-1);
    
    for (Entry e = tab[i];
         e != null;
         // 如果所在的下标已经有值了,则根据线性探测继续计算下一个下标值
         e = tab[i = nextIndex(i, len)]) {
        ThreadLocal<?> k = e.get();


        ...
    }
    ...
}
代码语言:javascript
复制
private static int nextIndex(int i, int len) {
    return ((i + 1 < len) ? i + 1 : 0);
}

根据以上的分析,ThreadLocal对象作为ThreadLocal.ThreadLocalMap中的key存放在Map中.(Map的底层是基于数组)

具体是根据ThreadLocal对象的threadLocalHashCode属性值进行取模计算出它在数组中的下标,假如ThreadLocal的threadLocalHashCode=1253254570,数组的默认长度是16,因此threadLocalHashCode & (16-1)=10,因此这个ThreadLocal对象应该放在数组的第10个位置.如果第10个位置已经有别的ThreadLocal对象霸占了,那么它就会尝试第11个位置是否有空缺,以此类推,直到找到一个空缺位置.

举例如下

代码语言:javascript
复制
import io.netty.util.concurrent.FastThreadLocal;

public class Address {
    // 定义了两个ThreadLocal对象
    public static final ThreadLocal<String> COMPANY = new ThreadLocal<String>() {
        @Override
        protected String initialValue() {
            return "CHINA";
        }
    };

    public static final ThreadLocal<Integer> YEAR = new ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
            return 1949;
        }
    };
}
代码语言:javascript
复制
Thread t1 = new Thread(() -> {
    System.out.println(Address.COMPANY.get());
    System.out.println(Address.YEAR.get());
    try {
        TimeUnit.MINUTES.sleep(15);// 只是为了让线程不终止
    } catch (InterruptedException ignored) { }
}, "thread-1");
t1.start();


Thread t2 = new Thread(() -> {
    System.out.println(Address.COMPANY.get());
    System.out.println(Address.YEAR.get());
    try {
        TimeUnit.MINUTES.sleep(15);// 只是为了让线程不终止
    } catch (InterruptedException ignored) { }
}, "thread-2");
t2.start();

根据代码和截图内容,我们总结下

线程2的哈希值1253254570的ThreadLocal存在索引10位置

线程2的哈希值-1401181199的ThreadLocal存在索引1位置

线程1的哈希值1253254570的ThreadLocal存在索引10位置

线程1的哈希值-1401181199的ThreadLocal存在索引1位置

JDK的ThreadLocal是根据它的哈希值然后再取模计算出索引位置,如果冲突还要再根据开放地址法-线性探测继续寻找下一个可用索引的位置.性能是比较低的. 那么我们看下在Netty中它是如何优化这个功能的呢?

代码语言:javascript
复制
import io.netty.util.concurrent.FastThreadLocalThread;

FastThreadLocalThread t3 = new FastThreadLocalThread(() -> {

    System.out.println(Address.FAST_COMPANY.get());
    System.out.println(Address.FAST_YEAR.get());

    try {
        TimeUnit.MINUTES.sleep(15);
    } catch (InterruptedException ignored) {

    }

}, "thread-3");
t3.start();

FastThreadLocalThread t4 = new FastThreadLocalThread(() -> {

    System.out.println(Address.FAST_COMPANY.get());
    System.out.println(Address.FAST_YEAR.get());

    try {
        TimeUnit.MINUTES.sleep(15);
    } catch (InterruptedException ignored) {

    }

}, "thread-4");
t4.start();

这段代码使用了Netty提供的FastThreadLocalThread线程,在它的内部有个自己的InternalThreadLocalMap,这个Map是与FastThreadLocal搭配使用的.

FastThreadLocal内部并不像JDK的ThreadLocal是根据哈希值与取模计算索引位置,它是在创建FastThreadLocal的时候就已经确定了索引位置,在JVM中每个FastThreadLocal的索引值都是不同的.

代码语言:javascript
复制
private final int index;
public FastThreadLocal() {
  // 创建FastThreadLocal时它的索引值index就确定下来了
  index = InternalThreadLocalMap.nextVariableIndex();
}

像Netty这样追求性能的底层网络框架,自己设计一个FastThreadLocalThread线程类,自己设计一个FastThreadLocal类,自己设计一个InternalThreadLocalMap类等.

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-11-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Netty历险记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档