NettyのFastThreadLocalソースのトレース

10217 ワード

前期準備ゼロ
0 FBI WARNING
文章はとてもくどくて回りくどい.
バージョン1
JDKバージョン:OpenJDK 11.0.1
IDE : idea 2018.3
Nettyバージョン:netty-all 4.1.34.Final
2 FastThreadLocalの概要
FastThreadLocalはNettyで実現される高性能ThreadLocalツールであり、機能的にはThreadLocalと差は少ないが、jdkが持参したThreadLocalよりはるかに性能的に高い.
3 Demo
import io.netty.util.concurrent.FastThreadLocal;

public class FastThreadLocalDemo {

    public static void main(String[] args) {

        //   FastThreadLocal   
        FastThreadLocal tl = new FastThreadLocal<>();

        //FastThreadLocal      
        long setBegin = System.nanoTime();
        tl.set("test");
        long setAfter = System.nanoTime();
        System.out.println("get : " + (setAfter - setBegin));

        //FastThreadLocal      
        long getBegin = System.nanoTime();
        String fastGet = tl.get();
        long getAfter = System.nanoTime();
        System.out.println("get : " + (getAfter - getBegin));

        //FastThreadLocal      
        long removeBegin = System.nanoTime();
        tl.remove();
        long removeAfter = System.nanoTime();
        System.out.println("remove : " + (removeAfter - removeBegin));
    }
}

FastThreadLocalの作成
Demoの作成コードに戻ります.
FastThreadLocal tl = new FastThreadLocal<>();

FastThreadLocalのコンストラクタのトレース:
//step 1
//FastThreadLocal.class
public FastThreadLocal() {
    //index     int      
    index = InternalThreadLocalMap.nextVariableIndex();
}

//step 2
//InternalThreadLocalMap.class
public static int nextVariableIndex() {
    //nextIndex        UnpaddedInternalThreadLocalMap       AtomicInteger     
    //UnpaddedInternalThreadLocalMap   InternalThreadLocalMap    
    //             int  ,   
    int index = nextIndex.getAndIncrement();
    if (index < 0) {
        nextIndex.decrementAndGet();
        throw new IllegalStateException("too many thread-local indexed variables");
    }
    return index;
}

FastThreadLocalの作成は、IDとして一意のint値を取得するだけで、他の操作はありません.
二InternalThreadLocalMap
InternalThreadLocalMapはFastThreadLocalの下位層で本当に機能するThreadLocalクラスであり,多くの静的方法を提供している.
1オブジェクトの取得
最も重要なのはInternalThreadLocalMapのget()メソッドです.
//InternalThreadLocalMap.class
public static InternalThreadLocalMap get() {
    //           
    Thread thread = Thread.currentThread();

    //         
    if (thread instanceof FastThreadLocalThread) {
        //  Netty      FastThreadLocal             
        return fastGet((FastThreadLocalThread) thread);
    } else {
        //          FastThreadLocal,        FastThreadLocalThread
        return slowGet();
    }
}

まずfastGet()を見てみましょう.
//InternalThreadLocalMap.class
private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) {
    //     FastThreadLocalThread    threadLocalMap        
    InternalThreadLocalMap threadLocalMap = thread.threadLocalMap();
    
    //        
    if (threadLocalMap == null) {
        thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap());
    }
    return threadLocalMap;
}

slowGet():
//InternalThreadLocalMap.class
private static InternalThreadLocalMap slowGet() {
    //   UnpaddedInternalThreadLocalMap    slowThreadLocalMap   
    //slowThreadLocalMap        ThreadLocal     ,         InternalThreadLocalMap
    ThreadLocal slowThreadLocalMap = UnpaddedInternalThreadLocalMap.slowThreadLocalMap;

    //        InternalThreadLocalMap        
    InternalThreadLocalMap ret = slowThreadLocalMap.get();

    //          
    if (ret == null) {
        ret = new InternalThreadLocalMap();
        slowThreadLocalMap.set(ret);
    }
    return ret;
}

実際にFastThreadLocalにとって、本当に役に立つのはInternalThreadLocalMapオブジェクトであることがわかります.
通常のスレッドでは,このオブジェクト自体はjdkのオリジナルサポートがないため,ThreadLocalオブジェクトにしかアタッチできない.
ただし、UnpaddedInternalThreadLocalMapのslowThreadLocalMap自体は静的ThreadLocalオブジェクトであるため、異なるスレッドが実際に呼び出されるのは同じオブジェクトであるが、取得されたInternalThreadLocalMapは同じではない.同じスレッドで複数のFastThreadLocalオブジェクトを作成すると、同じInternalThreadLocalMapが取得されます.
2 set
InternalThreadLocalMapの最下位ストレージはObject配列であり、setIndexedVariable(...)メソッドで格納されます.
//InternalThreadLocalMap.class
public boolean setIndexedVariable(int index, Object value) {
    //indexedVariables        UnpaddedInternalThreadLocalMap    Object   
    Object[] lookup = indexedVariables;

    //              
    //index     FastThreadLocal                ,          
    if (index < lookup.length) {
        //    
        Object oldValue = lookup[index];
        //  
        lookup[index] = value;
        //UNSET        Object   ,       lookup   
        //   oldValue      UNSET,                
        //          ,           ,    false
        return oldValue == UNSET;
    } else {
        //    
        expandIndexedVariableTableAndSet(index, value);
        return true;
    }
}

3 get
InternalThreadLocalMapで値を取得する方法は、indexedVariable(...)を使用します.
//InternalThreadLocalMap.class
public Object indexedVariable(int index) {
    //   index               
    Object[] lookup = indexedVariables;
    return index < lookup.length? lookup[index] : UNSET;
}

4 remove
InternalThreadLocalMapで値を削除する方法はindexedVariable(...)で行います.
//InternalThreadLocalMap.class
public Object removeIndexedVariable(int index) {
    //    
    Object[] lookup = indexedVariables;
    if (index < lookup.length) {
        Object v = lookup[index];
        //           UNSET   
        lookup[index] = UNSET;
        return v;
    } else {
        return UNSET;
    }
}

InternalThreadLocalMapには、自身を消去するための静的remove()メソッドもあります.
//InternalThreadLocalMap.class
public static void remove() {
    Thread thread = Thread.currentThread();
    if (thread instanceof FastThreadLocalThread) {
        ((FastThreadLocalThread) thread).setThreadLocalMap(null);
    } else {
        slowThreadLocalMap.remove();
    }
}

コードは比較的簡単で、あまり分析しません.
にメモリエレメント
Demoの格納要素のコードに戻ります.
tl.set("test");

追跡get(...)メソッド:
//step 1
//FastThreadLocal.class
public final void set(V value) {
    //    value    UNSET   
    if (value != InternalThreadLocalMap.UNSET) {
        //   InternalThreadLocalMap   
        InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();

        //setKnownNotUnset(...)      value    threadLocalMap  
        if (setKnownNotUnset(threadLocalMap, value)) {
            //         gc             
            registerCleaner(threadLocalMap);
        }
    } else {
        remove();
    }
}

//step 2
//FastThreadLocal.class
private boolean setKnownNotUnset(InternalThreadLocalMap threadLocalMap, V value) {
    //   setIndexedVariable(...)       value,      InternalThreadLocalMap      
    if (threadLocalMap.setIndexedVariable(index, value)) {
        //addToVariablesToRemove(...)      FastThreadLocal       threadLocalMap        
        //                 FastThreadLocal
        addToVariablesToRemove(threadLocalMap, this);
        return true;
    }
    return false;
}

三取得要素
Demoで取得した要素のコードに戻ります.
String fastGet = tl.get();

トレースset(...)メソッド:
//FastThreadLocal.class
public final V get() {
    //   InternalThreadLocalMap   
    InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
    //  InternalThreadLocalMap     
    Object v = threadLocalMap.indexedVariable(index);

    //     ,         
    if (v != InternalThreadLocalMap.UNSET) {
        return (V) v;
    }

    //       ,initialize(...)       null  
    V value = initialize(threadLocalMap);

    //gc   
    registerCleaner(threadLocalMap);

    //   null
    return value;
}

3要素の除去
Demoから要素を除去するコードに戻ります.
String fastGet = tl.get();

トレースremove(...)メソッド:
//step 1
//FastThreadLocal.class
public final void remove() {
    //InternalThreadLocalMap   getIfSet()       InternalThreadLocalMap   
    remove(InternalThreadLocalMap.getIfSet());
}

//step 2
//FastThreadLocal.class
public final void remove(InternalThreadLocalMap threadLocalMap) {

    //     
    if (threadLocalMap == null) {
        return;
    }

    //   
    Object v = threadLocalMap.removeIndexedVariable(index);

    //    threadLocalMap     FastThreadLocal            
    removeFromVariablesToRemove(threadLocalMap, this);

    if (v != InternalThreadLocalMap.UNSET) {
        try {
            //     ,          ,           
            onRemoval((V) v);
        } catch (Exception e) {
            PlatformDependent.throwException(e);
        }
    }
}

クアッドメモリ管理
実際の開発環境では、スレッドがこのFastThreadLocalを使用し、スレッドが完了した後にgcによって回収されたが、このFastThreadLocalの値が回収されていない場合があるかもしれない.
FastThreadLocalでは、メモリの漏洩を防ぐ方法registerCleaner(...):
//FastThreadLocal.class
private void registerCleaner(final InternalThreadLocalMap threadLocalMap) {

    Thread current = Thread.currentThread();

    //   FastThreadLocalThread         ,   index                   ,     
    if (FastThreadLocalThread.willCleanupFastThreadLocals(current) || threadLocalMap.isCleanerFlagSet(index)) {
        return;
    }

    //  index         
    threadLocalMap.setCleanerFlag(index);

    //                ,         
    //      ,     Netty        ,         4.1.34         
    //    ,               ,             ,      TODO   

//    ObjectCleaner.register(current, new Runnable() {
//        @Override
//        public void run() {
//            remove(threadLocalMap);
//        }
//   });
}

メーデーの小言
FastThreadLocalはjdkのオリジナルThreadLocalと比較して、性能の優位性は主に以下のいくつかの方面に現れます:
1、Netty          ,          ,           FastThreadLocal    
2、FastThreadLocal      ThreadLocal            Hash       ,      , 、        
3、ThreadLocal          FastThreadLocal     ,                 
4、FastThreadLocal    (static)         ,             

本文は個人の学習ノートのみで、誤りや表現がはっきりしないところがある可能性があります.