简单总结: 一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。各个线程中访问的是不同的对象。 总之,ThreadLocal不是用来解决对象共享访问问题的,而主要是提供了保持对象的方法和避免参数传递的方便的对象访问方式。归纳了两点: 一个ThreadLocal只能存放一个对象,网上有人讨论说: SINCE1978 写道 我想知道如果多次new ThreadLocal并且调用其set方法的话、是否就和普通hashmap一样后set进去的会覆盖先set进去的?这样的话ThreadLocal只能植入一个资源喽?这肯定不对,否则还用ThreadLocalMap这个自定义哈希表干什么,那么如何区分一个线程当中不同方法或不同类set进去的资源?并正确set和get?? 甚至还有人通过某些方式提供了一种一个ThreadLocal存放更丰富的对象比如Map,不用实例化太多thread local的方法,但是看了源码之后,我们会发现 初始化多次TheadLocal并没有多大的问题,也没有什么资源的浪费,一些人的误解可能是因为对ThreadLocalMap的误解:以为是一个ThreadLocal对应一个ThreadLocalMap,其实,是一个Thread对应一个ThreadLocalMap,所以在一个线程中创建多个ThredLocal实例的开销只有:set的时候,要将threadLocalHashCode 来计算哈希值,并将其放到线程实例唯一的ThreadLocalMap中,或者说是哈希函数计算后,放到数组中,,其他的开销,都在第一次set的时候就做了,就是创建一个线程唯一的ThreadLocalMap。 代码: public static ThreadLocal<Monkey> local=new ThreadLocal<Monkey>(); Monkey mm=new Monkey(); mm.a=1; mm.b=2; local.set(mm); Monkey mm2=local.get(); System.out.println(mm2.a); System.out.println(mm2.b); 源码解析: /** * ThreadLocals rely on per-thread linear-probe hash maps attached * to each thread (Thread.threadLocals and * inheritableThreadLocals). The ThreadLocal objects act as keys, * searched via threadLocalHashCode. This is a custom hash code * (useful only within ThreadLocalMaps) that eliminates collisions * in the common case where consecutively constructed ThreadLocals * are used by the same threads, while remaining well-behaved in * less common cases. */ private final int threadLocalHashCode = nextHashCode(); private static AtomicInteger nextHashCode = new AtomicInteger(); //1640531527 private static final int HASH_INCREMENT = 0x61c88647; private static int nextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT); } 可以看到另外两个变量时辅助作用的,threadLocalHashCode才是真正使用的,AtomicInteger使得程序可以使得其他应用以原子方式更新int的值,getAndAdd(HASH_INCREMENT)表示每次增加常理,1640531527。而threadLocalHashCode是在保存到ThreadLocalMap的时候,作为一个hash函数计算的时候使用的。 Get 再来看看get方法: public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } return setInitialValue(); } getMap方法,返回当前线程的ThreadLocalMap,它被声明为Thread的包级别的变量: threadLocals。 当然,一开始的时候他是null,那么他会被setInitialValue方法创建: private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; } Value一开始被设置为null,ThreadLocalMap被createMap方法创建: void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); } ThreadLocalMap /** * Construct a new map initially containing (firstKey, firstValue). * ThreadLocalMaps are constructed lazily, so we only create * one when we have at least one entry to put in it. */ ThreadLocalMap(ThreadLocal firstKey, Object firstValue) { //INITIAL_CAPACITY=16 table = new Entry[INITIAL_CAPACITY]; //哈希函数的计算 int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1; setThreshold(INITIAL_CAPACITY); } 顺便看下Entry的构造函数: static class Entry extends WeakReference<ThreadLocal> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal k, Object v) { super(k); value = v; } } 可以看到,ThreadLocal被作为一个弱引用,这样,一旦没有其他地方引用它,那么它就会在GC的时候被回收,当ThreadLocal被设置为null时,ThreadLocalMap就会移除key为ThreadLocal的Entry(因为Entry本身就是一个弱引用对象)。Hashtable/HashMap的实现方式则无法实现这一点。而且以前Hashtable的实现需要同步,所带来的性能损耗是很大的,而现在的方式则不需要同步。性能提升很大。。 ThreadLocal的set代码很少,可以看下面,不过其实真正的地方,是在ThreadLocalMap的set方法: /** * Set the value associated with key. * * @param key the thread local object * @param value the value to be set */ private void set(ThreadLocal key, Object value) { // We don't use a fast path as with get() because it is at // least as common to use set() to create new entries as // it is to replace existing ones, in which case, a fast // path would fail more often than not. Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1); for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { //引用的get方法,返回它所对应的那个对象 ThreadLocal k = e.get(); //1.如果已经set过值,那么替换 if (k == key) { e.value = value; return; } //2.如果已经被回收,那么重新设置值 if (k == null) { replaceStaleEntry(key, value, i); return; } } tab[i] = new Entry(key, value); int sz = ++size; //3..计算是否需要重新扩充容量 if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); } Set 接下来在看看Set: public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); } 可以看到这里有两步: 1.通过Thread获得他的ThreadLocalMap,这里可以看出每个Thread实例对应自己的一个ThreadLocalMap。 2.再将这个ThreadLocal作为key去保存set的这个value。 |
|