转载

ThreadLocal、InheritableThreadLocal详解

ThreadLocal、InheritableThreadLocal详解

多线程访问同一个共享变量时,容易出现并发冲突,为了保证线程的安全,一般使用者在访问共享变量时,需要进行适量的同步。而ThreadLocal提供了线程的私有变量,每个线程都可以通过set()get()来对这个私有变量进行操作,但不会和其他线程的私有变量进行冲突,实现了线程的数据隔离。InheritableThreadLocal作用和ThreadLocal相同,同时增加了一个功能,可以共享父线程InheritableThreadLocal提供的私有部变量,下面从源码角度,分别介绍这两个类。

ThreadLocal

  • 下面是TheadLocal一段简单使用的代码。线程a、线程b内部都维护了一个String类型变量,通过ThreadLocal 变量thl的get、set方法可以对其进行访问和修改。

    public class Main{
        static ThreadLocal<String> thl = new ThreadLocal<>();
        static void print(){
            System.out.println(thl.get());
        }
        public static void main(String args[]) throws Exception{
            Thread a = new Thread(new Runnable(){
                @Override
                public void run() {
                    thl.set("ThreadA variable");
                    print();
                }
            });
    
            Thread b = new Thread(new Runnable(){
                @Override
                public void run() {
                    thl.set("ThreadB variable");  
                    print();
                }
            });
            a.start(); b.start();
            a.join(); b.join();
        }
    }
    //结果可能是 ThreadA variable  ThreadB variable
    //或者是 ThreadB variable ThreadA variable
    
  • get、set方法

    每个线程里面均维护有threadLocals和inheritableLocals两个变量,两个变量均为ThreadLocalMap类(类似HashMap),调用ThreadLocal的set、get方法,其实就是对threadLocalMap进行修改和访问操作。同时也就解释了ThreadLocal提供的变量是线程私有的原因。

    public void set(T value) {
        //获取当前线程
        Thread t = Thread.currentThread();
        
        //获取线程的ThreadLocalMap变量
        ThreadLocalMap map = getMap(t);
        
        //key为ThreadLocal变量、value为T变量,添加进ThreadLocalMap变量
        if(map != null) map.set(this, value);
        else createMap(t,value);
    }
    
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if(map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if(e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
    }
    

InheritableThreadLocal

  • 下面这段代码,说明ThreadLocal不支持继承性

    public class TestThreadLocal{
    	public static ThreadLocal<String> threadLocal = new ThreadLocal<String>();
    	public static void main(String[] args) throws Exception {
     		threadLocal.set("Hello World");
            Thread t = new Thread(new Runnable(){
                 @Override
                 public void run() {
                     System.out.println("t:" + threadLocal.get());   
                 }
             });
         	t.start(); t.join();
         	System.out.println("Main:" + threadLocal.get());      
         }
    }
      //t:null
      //Main:Hello World
    
  • 而相比于ThreadLocalInheritableThreadLocal恰如其名称所描述的支持,子线程共享父线程InheritableThreadLocal提供的私有变量。

    下面解释InheritableThreadLocal的共享原理,线程在初始化的时候,如果父线程的inheritableThreadLocals不为空,则会调用createInheritedMap函数, 最终调用ThreadLocalMap的一个私有构造函数,用于将父线程的InheritableThreadLocal变量移植(直接复制引用,并没有重新开辟内存空间)到子线程的InheritableThreadLocal变量,达到一个变量共享的目的。

    //线程初始化函数
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
    private void init(ThreadGroup g, Runnable target, String name, long stackSize, AcessControlContext acc) {
        Thread parent = currentThread();
        if(parent.inheritableThreadLocals != null)
        	this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        	this.stackSize = stackSize;
        	tid = nextThreadID();
    }
    
    static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap){
        return new ThreadLocalMap(parentMap);
    }
    
    private ThreadLocalMap(ThreadLocalMap parentMap) {
        Entry[] parentTable = parentMap.table;
        int len = parentTable.length;
        setThreshold(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] = e;
                    size++;
                }
            }
            
        }
    }
    

参考资料

Java并发编程之美

正文到此结束
本文目录