美文网首页
关于ThreadLocal的说明

关于ThreadLocal的说明

作者: 华木公子 | 来源:发表于2019-07-30 15:35 被阅读0次

    一 ThreadLocal的使用场景

    当多个线程需要使用同一共享变量,但是每个线程又希望有自己独立的值,互不干扰时。

    二 ThreadLocal的原理

    1. 在Thread类中,有一个特殊的局部变量ThreadMap,这个Map不是通用Map,是单独构造的一个类似Map类的类。
    public
    class Thread implements Runnable {
    ...
    ThreadLocal.ThreadLocalMap threadLocals = null;
    ...
    }
    

    当Thread实例化时,一个thread就有一个对应的Map(私有)

    1. 当在thread线程中对threadlocal共享变量进行存值操作时,当前线程的私有Map会把threadlocal变量this作为key存储,值为对应需保存的value。
      当在thread线程中对threadlocal共享变量进行取值操作时,当前线程的私有Map会把threadlocal变量this作为key从map中取值,所获得的值为对应之前保留的value。
      由于map是私有的,所以其他线程是不可能操作这个线程的map,从而也就达到了互不影响的效果。

    【测试代码如下】:

    public class ThreadLocalDemo {
        private static ThreadLocal<String> myLocal = new ThreadLocal<String>();
    
        public static void main(String[] args) {
            Thread threadA = new Thread(new Runnable() {
                @Override
                public void run() {
                    myLocal.set("A");
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(myLocal.get());
                }
            });
            myLocal.set("main");
            threadA.start();
            try {
                threadA.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(myLocal.get());
        }
    }
    

    当对myLocal.set("A");执行时,ThreadLocal代码如下:

    public class ThreadLocal<T> {
    public void set(T value) {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
        }
    
    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;
                }
            }
            return setInitialValue();
        }
    }
    
    1. 一个threadlocal变量对应一个值,一个线程中可以使用多个threadlocal变量来存取多个值。但是一个线程只有一个map,所以可能会存在key冲突的问题,那么当key的hashcode冲突时,就会继续往下一个数组空间存放,直到不冲突。这就存在效率问题,取值时,就需要根据hashcode比较,取出threadlocal对象后还要比较(==)是否是同一个对象,如果是同一个对象,才会去取它对应的值。
      ThreadLocal.ThreadLoclMap 的代码如下:
     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)]) {
                    ThreadLocal<?> k = e.get();
    
                    if (k == key) {
                        e.value = value;
                        return;
                    }
    
                    if (k == null) {
                        replaceStaleEntry(key, value, i);
                        return;
                    }
                }
    
                tab[i] = new Entry(key, value);
                int sz = ++size;
                if (!cleanSomeSlots(i, sz) && sz >= threshold)
                    rehash();
            }
    

    三 ThreadLocal使用的注意事项

    1. 内存泄漏问题
      threadmap的entry本身key是弱引用可以被回收,但是value是强引用,不会被GC回收。如果使用了线程池,那么value就会一直存在,导致内存泄漏。

    相关文章

      网友评论

          本文标题:关于ThreadLocal的说明

          本文链接:https://www.haomeiwen.com/subject/arlnrctx.html