ThreadLocal

ThreadLocal小谈

ThreaLocal这个类型经常用来实现应用上下文,应用上下文可以多线程共享的。常常在面试中也能听到”请你谈谈ThreadLocal与多线程的应用?” 我认为ThreadLocal和多线程的应用基本没有什么关系,而是在单线程中的应用。因为在线程A中对ThreadLocal操作,线程B根本是无法感知的。ThreadLocal无法在线程之间传递数据(具体分析ThreadLocal的源代码)。ThreadLocal用来隔离各个线程。

代码分析

描述Thread,ThreadLocal,ThreadLocal.ThreadLocalMap这3个类中的关键属性和方法,来简单分析ThreadLocal的原理。代码全部来自jdk1.7,省略了一部分代码,自己加了注释。

Thread类

class Thread{
    //这个是每个线程相互独立的关键,线程之间不共享
    //默认的访问权限,同包名可访问
    ThreadLocal.ThreadLocalMap threadLocals = null;
}

ThreadLocal类及其静态内部类ThreadLocalMap

class ThreadLocal{
    //对外提供的API看不出线程隔离,内部是取当前线程的ThreadLocalMap
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            //以ThreadLocal为key
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        //如果没有,就设置初始值null
        return setInitialValue();
    }

    /**
    * 获取当前线程的ThreadLocalMap,真正用来存储上下文的地方,每个线程相互独立
    */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

    /**
    * Variant of set() to establish initialValue. Used instead
    * of set() in case user has overridden the set() method.
    *
    * @return the initial value
    */
    private T setInitialValue() {
        T value = initialValue();//初始值为null
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

    /**
    * 每次线程中的ThreadLocalMap都是new出来的,所以相互独立
    */
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

    //这个就像是map,当超过了阈值就要rehash
    //key为ThreadLocal,线程内不同的ThreadLocal值,value也是不一样的。
    static class ThreadLocalMap{
        //entry继承自弱引用,可以被gc回收
        static class Entry extends WeakReference<ThreadLocal> {
            /** The value associated with this ThreadLocal. */
            Object value;

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

        /**
         * The table, resized as necessary.
         * table.length MUST always be a power of two.
         */
        private Entry[] table;
    }
}

测试代码

同一个ThreadLocal在不同的线程中相互独立。

public class ThreadLocalTester {
    public static void main(String[] args) {
        ThreadLocal threadLocal = new ThreadLocal<Integer>();

        new Thread(new ThreadLocalHolder(threadLocal, false)).start();
        new Thread(new ThreadLocalHolder(threadLocal, true)).start();
    }
}

class ThreadLocalHolder implements Runnable {

    private ThreadLocal<Integer> threadLocal;

    private boolean dir;

    public ThreadLocalHolder(ThreadLocal<Integer> thlocal, boolean dir) {
        this.threadLocal = thlocal;
        this.dir = dir;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run() {
        while (true) {
            Integer integer = threadLocal.get();
            if (null == integer) {
                threadLocal.set(0);
            } else {
                int i = integer.intValue();
                i = dir ? i - 1 : i + 1;
                threadLocal.set(i);
            }
            System.out.println(Thread.currentThread().getName() + "  i is: " + threadLocal.get());
            try {
                Thread.currentThread().sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

特点分析

  • ThreadLocal内部定义了一个静态类ThreadLocalMap这个类专门用来存放信息(非静态的内部类能访问外部类的资源),Thread中有个类型为ThreadLocalMap的默认访问级别的成员变量threadlocals,这以为着每个线程之间的threadLocals变量是相互独立的,而同一个线程内都是共享的
  • ThreadLocalMap key的类型为ThreadLocal,这点应该是为专门给ThreadLocal使用的,ThreadLoaclMap这个class的访问级别也是默认级别。放入别的key就会产生编译错误。
  • 其实ThreadLocalMap里面的entry类型为entry(ThreadLocal, Object),new ThreadLoacl()出来的threadlocal都是不一样的(key不同了),意味着不同的threadlocal里面的value值都是不同,实现了一个线程中存储多个值。
  • 一个static的ThreadLocal变量,在不同的线程中或者的值都是不同的。因为ThreadLocal是从不同的线程中取值(getMap(){t.threadLocalMaps})。厉害
  • 一个ThreadLocal对应一个键值对,ThreadLocalMap中的key为Threadlocal
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值