Netty的FastThreadLocal

2022-06-02 13:57:21 浏览数 (1)

在文章ThreadLocal与FastThreadLocal中我们介绍了JDK的ThreadLocal与Netty的FastThreadLocal之间的一点区别.

这篇文章在单独介绍下FastThreadLocal.

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

我们先看个对比表格

JDK

Netty

Thread

FastThreadLocalThread

ThreadLocal

FastThreadLocal

ThreadLocalMap

InternalThreadLocalMap

在Netty中,每个被创建的FastThreadLocal对象,都与唯一的一个index值所绑定,比如说在JVM中,第一个被创建的FastThreadLocal,它的index属性值=1,第二个被创建的FastThreadLocal,它的index属性值=2,第120个被创建的FastThreadLocal,它的index属性值=120,以此类推

于是,每个FastThreadLocal作为key(实际是作为数组下标),被存放到FastThreadLocalThread的InternalThreadLocalMap中时,它的位置已经被固定了.

代码语言:javascript复制
// FastThreadLocal构造器

private final int index;
public FastThreadLocal() {
    index = InternalThreadLocalMap.nextVariableIndex();
}

// 静态属性
static final AtomicInteger nextIndex = new AtomicInteger();
public static int nextVariableIndex() {
    int index = nextIndex.getAndIncrement();
    return index;
}

在InternalThreadLocalMap内部,底层使用的是对象数组存储数据的.

代码语言:javascript复制
// 底层使用数组存储数据
Object[] indexedVariables;

测试代码如下

代码语言:javascript复制
public static final FastThreadLocal<String> FAST_COMPANY = new FastThreadLocal<String>() {
    @Override
    protected String initialValue() {
        return "FAST_CHINA";
    }
};

public static final FastThreadLocal<Integer> FAST_YEAR = new FastThreadLocal<Integer>() {
    @Override
    protected Integer initialValue() {
        return 2020;
    }
};
代码语言:javascript复制
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();

我们创建了两个静态的FastThreadLocal对象,它们的索引分别是1和2,因此它们被存放到了线程的InternalThreadLocalMap属性的下标1和2的位置.

这时候我们关注的另一个点是,这个InternalThreadLocalMap,它的底层是数组,用来存储数据的.数组默认大小32.

代码语言:javascript复制
private static Object[] newIndexedVariableTable() {
    Object[] array = new Object[32];
    Arrays.fill(array, UNSET);
    return array;
}

由于我们的测试代码在整个JVM中只是创建了2个FastThreadLocal对象.而测试的两个线程也只是使用了这2个FastThreadLocal对象.

我们现在测试这么一个场景,在JVM中,在其他的地方创建了200个FastThreadLocal对象,在创建完这200个FastThreadLocal对象之后,又创建了我们的那2个FastThreadLocal对象.也就是说我们的那2个FastThreadLocal对象的索引分别是201和202.

代码语言:javascript复制
static {
   // 模拟前提前创建200个FastThreadLocal对象
   for (int i =0;i <200;i  ) {
       final int j = i;
       FastThreadLocal<String> FAST_COMPANY = new FastThreadLocal<String>() {
           @Override
           protected String initialValue() {
               return "FAST_CHINA"   j;
           }
       };
   }
}


public static void main(String[] args) {

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

再次测试我们的代码

2个FastThreadLocal对象被存放在索引201和202位置.

InternalThreadLocalMap整个数组大小是256

线程为了存放2个FastThreadLocal对象,需要使用256长度的数组,这无疑是一种空间换时间.

0 人点赞