ThreadLocal


作用:

用于数白了就是线能操作自己的量,避免了冲突问题

 

原理:

实现原理的核心就是ThreadLocalMap

 

1 thread里面维护着一个ThreadLocalMap

/* ThreadlocaZ values pertaining to this thread. This nFp is mintained  * by the Threadlocal class. */  ThreadLocal. ThreadLocalMap threadLocals = null;

 

2 ThreadLocalMap 是一个定在 ThreadLocal 中的内部,是一个 map,用Entry 来行数

static class ThreadLocatMap {  The entries in this hash map extend WeakReference, using its main ref field as the  always a ThreadLocal object). Note that null keys (I.e. entry.get() null) mean th  longer referenced, so the entry can be expunged from table. Such entries are reft  ?stale entries ? in the code that follows.  static class Entry extends WeakReference<ThreadLocal<?>> {  The value associated with this ThreadLocal.  Object value;  Entry(ThreadLocal<?> k,  super (k);  value = v;  Object v) {

 

ThreadThreadLocal ThreadLocalMap系就是实现原理

ThreadLocaIMap 1  ThreadLocal 1  ThreadLocal 2  ThreadLocal n  ThreadLocaIMap 2  ThreadLocal 1  ㄒ h 「 eadLoc 刨 2  readLocal m

? 个ThreadLocal

个ThreadLocal象都有一个hashthreadLocalHashCode,初始化一个ThreadLocal象,hash加一个固定的大小0x61c88647

? 实际需要的成员变

 

 

常用方法:

1 . public T get() 取当前线程的副本

用 ThreadLocal 的 get() 方法,也是先取当前线程的 ThreadLocalMap 象,以 ThreadLocal 象作 key

 

ThreadLocalMap 中

// 取当前线程中的量副本
public T get() {

    // 取当前线

    Thread t = Thread.currentThread();

    // 线程中的ThreadLocalMap

    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;

        }

    }

    // 若没有该变量副本,返回setInitialValue()

    return setInitialValue();

}

 

2 . public void set(T value) 保存当前线程的副本

用 ThreadLocal 的 set() 方法,先取当前线程的 ThreadLocalMap 象,然后以 ThreadLocal 象作 key 往ThreadLocalMap 中

注意:个ThreadLocal象都有一个hashthreadLocalHashCode,初始化一个ThreadLocal象,hash加一个固定的大小0x61c88647

private final int threadLoca1HashCode = nextHashCode();  The next hash code to be given out. Updated atomically. Starts at zero.  private static Ataniclnteger nextHashCode =  new Ataniclnteger();  The difference between successively generated hash codes - turns implicit sequential thread-local  IDs into near-optimally spread multiplicative hash values for power-of-two-sized tables.  private static final int HASH_INCREMENT = 8x61c88647;  Returns the next hash code.  private static int nextHashCode  { return nextHashCode.getAndAdd(HASH_INCREMENT);

 

public void set(T value) {

    Thread t = Thread.currentThread();

    ThreadLocalMap map = getMap(t);

    if (map != null)

        // map不空,直接将ThreadLocal象作key

        // 量本身的值为value,存入map

        map.set(this, value);

    else

        // 否建ThreadLocalMap

        createMap(t, value);

}

 

ThreadLocatMap getMap(Thread t) {  return t. threadLocals;

 

GET程:

在get的候,也会根ThreadLocal象的hash,定位到table中的位置,然后判断位置Entry象中的key是否和get的key一致,如果不一致,就判断下一个位置,set和get如果冲突重的低的。

private Entry getEntry(ThreadLocal<?> key) {

            int i = key.threadLocalHashCode & (table.length - 1);

            Entry e = table[i];

            if (e != null && e.get() == key)

                return e;

            else

                return getEntryAfterMiss(key, i, e);

        }

 

 private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {

            Entry[] tab = table;

            int len = tab.length;

// get的候一是根ThreadLocal取到table的i,然后查找据拿到后会比key是否相等  if (e != null && e.get() == key)。

            while (e != null) {

                ThreadLocal<?> k = e.get();

              // 相等就直接返回,不相等就继续查找到相等位置。

                if (k == key)

                    return e;

                if (k == null)

                    expungeStaleEntry(i);

                else

                    i = nextIndex(i, len);

                e = tab[i];

            }

            return null;

        }

 

 

景:

1 Spring采用Threadlocal的方式,来保证单线程中的数操作使用的是同一个数库连接.

2 们每操作,都会走JDBC getConnection,JDBC保是同一个线来的求,不管是在个part,都返回的是同一个接。个就是使用ThreadLocal来的。

3 ThreadLocal给每线程分配一个SimpleDateFormat象。

4 全局量的传递,通常可以通AOP或者器的方式赋值行完业务逻辑之后用remove()方法。

5 Mybatis使用SqlSessionManager保了我同一个线程取出来的是同一个。就是内部使用了一个ThreadLocal。

 

 

内存露:

m??J靄|

 

由于ThreadLocalMap的key是弱引用,而Value是引用。致了一个问题,ThreadLocal在没有外部引用生GC弱引用Key会被回,而Value不会回,如果建ThreadLocal的线程一直持续运行,那么这个Entry象中的value就有可能一直得不到回生内存露。