java 从ThreadLocal到MDC再到SkyWalling

举报
赵KK日常技术记录 发表于 2023/06/29 21:24:56 2023/06/29
【摘要】 应用在日常项目中,为了记录一个服务的调用与输出日志,我们通常会使用AOP的方式拦截记录参数,这在平时的问题溯源过程中起到了极大的作用,但在调用链系统应用中大多使用多线程的方式,所以很多链路追踪的组件就应运而生。ThreadLocalThis class provides thread-local variables. These variables differ from * their ...


应用

在日常项目中,为了记录一个服务的调用与输出日志,我们通常会使用AOP的方式拦截记录参数,这在平时的问题溯源过程中起到了极大的作用,但在调用链系统应用中大多使用多线程的方式,所以很多链路追踪的组件就应运而生。

ThreadLocal

This class provides thread-local variables.  These variables differ from
 * their normal counterparts in that each thread that accesses one (via its
 * {@code get} or {@code set} method) has its own, independently initialized
 * copy of the variable.  {@code ThreadLocal} instances are typically private
 * static fields in classes that wish to associate state with a thread (e.g.,
 * a user ID or Transaction ID).

threadlocal是为了绑定当前线程,并不是为了保证线程安全,而是为了在线程内方便共享变量跨方法的传递,如果跨线程传递其本身实例就比较繁琐了,其副本是可以跨线程传递的,我们可以写一个简单例子看一下

package com.atkk.kk.netty.websocket;

public class ThreadLocalDemo {
    public static ThreadLocal<Order> orderThreadLocal = new ThreadLocal<>();

    public static void main(String[] args) {
        Order order = new Order();
        System.out.println(order.hashCode());
        orderThreadLocal.set(order);
        EntityBussiness1 entityBussiness1 = new EntityBussiness1();
        其他线程下是隔离的
        new Thread(()->{
            EntityBussiness2 entityBussiness2 = new EntityBussiness2();
            entityBussiness2.run();
        }).start();
        entityBussiness1.run();
    }

    static class EntityBussiness1{
        public void run(){
            Order order = orderThreadLocal.get();
            if(order == null){
                System.out.println("threadlocal == null in thread: " + Thread.currentThread().getName());
                return;
            }
            System.out.println(order.hashCode());
        }
    }

    static class EntityBussiness2{
        public void run(){
            Order order = orderThreadLocal.get();
            if(order == null){
                System.out.println("threadlocal == null in thread: " + Thread.currentThread().getName());
                return;
            }
            System.out.println(order.hashCode());
        }
    }
}

控制台输出

205125520
205125520
threadlocal == null in thread: Thread-0

而为了解决父子线程之间私有变量问题,InheritableThreadLocal应运而生。

InheritableThreadLocal

源码

public class InheritableThreadLocal<T> extends ThreadLocal<T> {
    /**
     * Computes the child's initial value for this inheritable thread-local
     * variable as a function of the parent's value at the time the child
     * thread is created.  This method is called from within the parent
     * thread before the child is started.
     * <p>
     * This method merely returns its input argument, and should be overridden
     * if a different behavior is desired.
     *
     * @param parentValue the parent thread's value
     * @return the child thread's initial value
     */
    protected T childValue(T parentValue) {
        return parentValue;
    }

    /**
     * Get the map associated with a ThreadLocal.
     *
     * @param t the current thread
     */
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }

    /**
     * Create the map associated with a ThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the table.
     */
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}


当在InheritableThreadLocal点击thread跳转时会直接跳转到thread的构造函数

public Thread(ThreadGroup group, Runnable target, String name,
              long stackSize, boolean inheritThreadLocals) {
    this(group, target, name, stackSize, null, inheritThreadLocals);
}

注意是线程,不是线程池,这个构造函数有些陌生。


还有这里的Entry在后续仍会用到

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}

所谓的线程的初始化可能会有些出入

/**
 * Allocates a new {@code Thread} object. This constructor has the same
 * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
 * {@code (null, null, gname)}, where {@code gname} is a newly generated
 * name. Automatically generated names are of the form
 * {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
 */
public Thread() {
    this(null, null, "Thread-" + nextThreadNum(), 0);
}

传递参数的原因

if (inheritThreadLocals && parent.inheritableThreadLocals != null)
    this.inheritableThreadLocals =
        ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

核心代码

/**
 * Construct a new map including all Inheritable ThreadLocals
 * from given parent map. Called only by createInheritedMap.
 *
 * @param parentMap the map associated with parent thread.
 */
private ThreadLocalMap(ThreadLocalMap parentMap) {
    Entry[] parentTable = parentMap.table;
    int len = parentTable.length;
    setThreshold(len);
    table = new Entry[len];

    for (Entry e : parentTable) {
        if (e != null) {
            @SuppressWarnings("unchecked")
            ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
            if (key != null) {
                Object value = key.childValue(e.value);
                Entry c = new Entry(key, value);
                int h = key.threadLocalHashCode & (len - 1);
                while (table[h] != null)
                    h = nextIndex(h, len);
                table[h] = c;
                size++;
            }
        }
    }
}
        /**
         * 构建一个包含所有parentMap中Inheritable ThreadLocals的ThreadLocalMap
         * 该函数只被 createInheritedMap() 调用.
         */
        private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];
            for (int j = 0; j < len; j++) {
                Entry e = parentTable[j];
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                    if (key != null) {
                        Object value = key.childValue(e.value);
                        Entry c = new Entry(key, value);
                        int h = key.threadLocalHashCode & (len - 1);
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。