TransmittableThreadLocal使用

问题分析

ThreadLocal: 解决多线程变量之间的隔离, 每个线程对变量做了一个副本

InheriableThreadLocal: 解决了父线程变量复制到子线程的问题

class Thread{
    //...略
    private void init(ThreadGroup g, Runnable target, String name,
                          long stackSize, AccessControlContext acc) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name.toCharArray();

        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
            /* Determine if it's an applet or not */

            /* If there is a security manager, ask the security manager
               what to do. */
            if (security != null) {
                g = security.getThreadGroup();
            }

            /* If the security doesn't have a strong opinion of the matter
               use the parent thread group. */
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }

        /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
        g.checkAccess();

        /*
         * Do we have the required permissions?
         */
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }

        g.addUnstarted();

        this.group = g;
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        this.target = target;
        setPriority(priority);
        
        
        //###############################重点##############################################
        //重点地方: 如果当前线程的inheritableThreadLocals 不为空的时候, 
        // 将父线程的inheritableThreadLocals 复制到子线程的LocalMap,从而解决了父线程变量复制到子线程的问题
        if (parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* Set thread ID */
        tid = nextThreadID();
    }
}

那么TransmittableThreadLocal解决了什么问题呢? 如果思考这个问题的时候,注意上面的代码只有父线程里面 创建一个 线程(Thread) 的时候才回去调用 init 才会有inheritableThreadLocals的拷贝动作 如果不是建立一个线程呢, 如果是采用线程池来运行子线程的任务(runnable)呢? inheriableThreadLocal 还可以解决问题吗? demo如下:

public class InheritableThreadLocalTestUpdate {
    static final ThreadLocal<Integer> inheritableThreadLocal = new InheritableThreadLocal<Integer>();
    static ExecutorService pool = Executors.newFixedThreadPool(2);

//    static TransmittableThreadLocal<Integer> inheritableThreadLocal = new TransmittableThreadLocal<>();
//    static ExecutorService pool = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(5));


    static class MainThread extends Thread {

        private int index;

        public MainThread(int index) {
            this.index = index;
        }

        @Override
        public void run() {

            inheritableThreadLocal.set(index);
            pool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("child inheritableThreadLocal: " + inheritableThreadLocal.get());
                }
            });
        }
    }
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new MainThread(i).start();
        }
    }
}

打印结果: child inheritableThreadLocal: 1 child inheritableThreadLocal: 1 child inheritableThreadLocal: 1 child inheritableThreadLocal: 1 child inheritableThreadLocal: 1 child inheritableThreadLocal: 0 child inheritableThreadLocal: 0 child inheritableThreadLocal: 0 child inheritableThreadLocal: 0 child inheritableThreadLocal: 0 child inheritableThreadLocal: 0 child inheritableThreadLocal: 0 child inheritableThreadLocal: 0 child inheritableThreadLocal: 0 child inheritableThreadLocal: 0 child inheritableThreadLocal: 0 child inheritableThreadLocal: 0 child inheritableThreadLocal: 0 child inheritableThreadLocal: 0 child inheritableThreadLocal: 0

结果显示不是我们要的结果: 结论: 当使用线程池来运行我们的子线程的任务的时候,采用InheriableThreadLocal是无法解决变量传递的问题的

解决办法: 采用阿里推出的transmitableThreadLocal 很好的解决了这个问题

public class TransmittableThreadLocal<T> extends InheritableThreadLocal<T> {

    @Override
    public final T get() {
        T value = super.get();
        if (null != value) {
            addValue();
        }
        return value;
    }

    @Override
    public final void set(T value) {
        super.set(value);
        if (null == value) { // may set null to remove value
            removeValue();
        } else {
            //如果值不为空的时候,添加到holder
            addValue();
        }
    }

    @Override
    public final void remove() {
        removeValue();
        super.remove();
    }

    void superRemove() {
        super.remove();
    }

    T copyValue() {
        return copy(get());
    }

    private static InheritableThreadLocal<Map<TransmittableThreadLocal<?>, ?>> holder =
            new InheritableThreadLocal<Map<TransmittableThreadLocal<?>, ?>>() {
                @Override
                protected Map<TransmittableThreadLocal<?>, ?> initialValue() {
                    return new WeakHashMap<TransmittableThreadLocal<?>, Object>();
                }

                @Override
                protected Map<TransmittableThreadLocal<?>, ?> childValue(Map<TransmittableThreadLocal<?>, ?> parentValue) {
                    return new WeakHashMap<TransmittableThreadLocal<?>, Object>(parentValue);
                }
            };

    private void addValue() {
        if (!holder.get().containsKey(this)) {
            //将值放到holder
            holder.get().put(this, null); // WeakHashMap supports null value.
        }
    }
}

execute 的时候怎么获取值呢?

public final class TtlRunnable implements Runnable {
    private final AtomicReference<Map<TransmittableThreadLocal<?>, Object>> copiedRef;
    private final Runnable runnable;
    private final boolean releaseTtlValueReferenceAfterRun;

    private TtlRunnable(Runnable runnable, boolean releaseTtlValueReferenceAfterRun) {
        this.copiedRef = new AtomicReference<Map<TransmittableThreadLocal<?>, Object>>(TransmittableThreadLocal.copy());
        this.runnable = runnable;
        this.releaseTtlValueReferenceAfterRun = releaseTtlValueReferenceAfterRun;
    }

    /**
     * wrap method {@link Runnable#run()}.
     */
    @Override
    public void run() {
        //获取WeekReference 弱引用
        Map<TransmittableThreadLocal<?>, Object> copied = copiedRef.get();
        if (copied == null || releaseTtlValueReferenceAfterRun && !copiedRef.compareAndSet(copied, null)) {
            throw new IllegalStateException("TTL value reference is released after run!");
        }

        Map<TransmittableThreadLocal<?>, Object> backup = TransmittableThreadLocal.backupAndSetToCopied(copied);
        try {
            //like apo around , before dothing, after dothing 
            runnable.run();
        } finally {
            TransmittableThreadLocal.restoreBackup(backup);
        }
    }
}

使用如下

添加依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>transmittable-thread-local</artifactId>
    <version>2.2.0</version>
</dependency>
package com.example.demo.test;

import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.threadpool.TtlExecutors;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @date 2019-1-9
 * @description 作用 多线程提交任务的时候会threadLocal导致传递的变量失效
 */
public class TransmitTableThreadLocalTest {

    static TransmittableThreadLocal<String> threadLocal = new TransmittableThreadLocal<>();
    static ExecutorService pool = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(5));


    //如果采用jdk的threadLocal 难以保证在线程池执行的时候变量的传递
    //static ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
    //static ThreadLocal<String> threadLocal = new ThreadLocal<>();
    //static ExecutorService pool = Executors.newFixedThreadPool(2);

    public static void main(String[] args) {


        for (int i = 0; i < 100; i++) {
            int index = i;
            pool.execute(new Thread(new Runnable() {

                @Override
                public void run() {

                    //传递值
                    threadLocal.set("java " + index);

                    pool.execute(new Thread(
                            new Runnable() {

                                @Override
                                public void run() {
                                    //获取值
                                    System.out.println(Thread.currentThread().getName() + " 获取修改后的值:" + threadLocal.get());
                                }
                            }
                    ));
                }
            }));
        }

        try {
            Thread.sleep(1000);
            pool.shutdown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }
}