极速赛车APP下载

再看ThreadLocal丶一个站在Java后端设计之路的男青年个人博客网站

电脑杂谈  发布时间:2019-08-31 10:02:04  来源:网络整理

static threadlocal_threadlocal static_threadlocal set

ThreadLocal,网上文章太多,大家也基本就会使用,但是不必定用的好,或者说不必定真的能理解

ThreadLocal本身并不作为存储的容器,而是把值保存在当前线程中的数组后面threadlocal static,Thread类里如下:

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
    /*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

有人会奇怪为什么会有2个对象,threadLocals就是我们通常状况使用的,而inheritableThreadLocals是供在当前线程又开了一个子线程时用的,这样可以使子线程也可以使用当前线程的threadLocals对象,需要配合使用ThreadLocal的子类InheritableThreadLocal使用,具体看下一节

threadLocals为空,翻遍了Thread内的源码threadlocal static极速赛车APP下载,你也找不到给他赋值的地方,因为赋值的地方在ThreadLocal这个类上面:

    /**
     * Create the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the map
     */
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

如上所示,给当前thread的threadLocals变量数组,并且key是this,也就是当前的ThreadLocal对象

注意到使用的是ThreadLocalMap,这个类很像HashMap,但是它并未实现任何接口,这个类在ThreadLocal里面,虽然定义成成static class ThreadLocalMap,但是它的方式都是private的,意味着这个类只供ThreadLocal使用

static threadlocal_threadlocal set_threadlocal static

极速赛车APP下载其外部使用自定义的Entry对象来存储数据,Entry类如下:

static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

继承了WeakReference,看构造方式,可以了解key是WeakReference类型,value是普通的强引用

这意味着,如果没有其他引用,那么泛型结束后,key会被手动回收,也即ThreadLocal会被回收,看起来很完美

但是通常状况下,我们的使用方式,都是类似如下:

public abstract class ThreadContext {
    private static final ThreadLocal<Map<Object, Object>> resources = new InheritableThreadLocalMap<>();
}

极速赛车APP下载使用static修饰,那么如果线程结束,依然有引用,所以不会被回收

而且,很多之后我们使用线程池,可能线程依然都不会结束,那么ThreadLocal对象也就依然不会被回收

threadlocal set_threadlocal static_static threadlocal

上述两种状况都有也许出现内存泄漏,而且会出现逻辑混乱的现象,所以最佳实践就是:在使用完后,显示的调用ThreadLocal的remove方法

InheritableThreadLocal的通常使用方式如下:

public class Test {
 
    public static ThreadLocal<Integer> threadLocal = new InheritableThreadLocal<Integer>();
 
    public static void main(String args[]){
        threadLocal.set(new Integer(123));
 
        Thread thread = new MyThread();
        thread.start();
 
        System.out.println("main = " + threadLocal.get());
    }
 
    static class MyThread extends Thread{
        @Override
        public void run(){
            System.out.println("MyThread = " + threadLocal.get());
        }
    }
}

输出:

main = 123
MyThread = 123

子线程完美的使用了父线程的对象,这是怎样实现的呢?

极速赛车APP下载InheritableThreadLocal类很简单,源码如下:

public class InheritableThreadLocal<T> extends ThreadLocal<T> {
    protected T childValue(T parentValue) {
        return parentValue;
    }
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}

threadlocal set_static threadlocal_threadlocal static

继承了ThreadLocal,覆盖了父类的getMap方法,返回的是Thread类的inheritableThreadLocals成员变量,也就是我们之前看到的Thread累里面的另一个成员变量

同时也覆盖了createMap方法,赋值的只是父类的inheritableThreadLocals成员变量

可以看到InheritableThreadLocal里面操作的都是父类的inheritableThreadLocals了,和Thread的成员函数threadLocals没有任何关系了,这里虽然用到了模版路径,父类定义了一个流程,子类按照父类的框架执行一个定义好的步骤,只是一些细节可以有自己的实现

我们看一下Thread的构造方式,会读取各种init方法,这些init办法最后都是调用上面的init方法,而且最后一个参数inheritThreadLocals为true(严谨一点,Thread(Runnable target, AccessControlContext acc)这个构造方法调用init方法的inheritThreadLocals为false,但是该方式不是public的)

private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
  ...
    Thread parent = currentThread();
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
  ...
}
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
}

和inheritableThreadLocals相关的代码如上,如果inheritThreadLocals为true,并且当前线程的inheritableThreadLocals值不为空,那么给inheritableThreadLocals赋值,我们再看一下调用的new ThreadLocalMap构造方法

private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[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] = c;
                        size++;
                    }
                }
            }
        }

可发现,是一个遍历数组的操作,看过HashMap源码的都明白,里面的Entry是一个链表,因为会有key重复的状况,这里为什么没有呢?

threadlocal set_threadlocal static_static threadlocal

大家看16,17行,如果位置为h的早已有值了,那么死循环,重新生成位置h,直到该位置没有值,所以不需要链表

这样子线程就有用父线程的对象了,需要注意的是,第13行,Object value = key.childValue(e.value);

这个方式在ThreadLocal里如下:

T childValue(T parentValue) {
        throw new UnsupportedOperationException();
    }

因为这个方式就不是给他用的,而是给他的子类InheritableThreadLocal用的,该方式在InheritableThreadLocal覆盖如下:

protected T childValue(T parentValue) {
        return parentValue;
    }

只是简单的返回参数,一般状况下够用了,但是一旦你的需求是想子线程和父线程的值可以各自设置而不受影响,那么他们可以继承InheritableThreadLocal这个类,覆盖其childValue方法,在该方式里你就可以进行深度拷贝的操作,注意是深度拷贝

通过如上分析,也可以看起来

可以再同一个线程里同时使用ThreadLocal和InheritableThreadLocal,他们对应的是Thread里的不同变量,互不影响,只是InheritableThreadLocal可以被子类继承 可以使用多个ThreadLocal对象,互不影响,因为源码里的ThreadLocalMap的key是ThreadLocal本身,所以,很没看ThreadLocal源码前,可能会以为key是当前Thread,如果是的话,就不能同时使用多个ThreadLocal对象了,后面的覆盖上面的 使用InheritableThreadLocal时可以覆盖childValue方法,进行深度拷贝

欢迎来到梁钟霖个人博客网站。本个人博客网页提供最新的站长新闻,各种互联网资讯。还提供个人博客模板,最新最全的java教程,java面试题。在此我将尽我最大所能将此个人博客网站做的最好!谢谢你们,愿各位一起进步!


本文来自电脑杂谈,转载请注明本文网址:
http://www.0531mai.com/a/jisuanjixue/article-121195-1.html

    相关阅读
    发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

    极速赛车手机官网 极速赛车APP 极速赛车手机版下载 极速赛车双面盘 极速赛车手机官网 极速赛车双面盘 极速赛车手机官网 极速赛车双面盘 极速赛车手机版下载 极速赛车APP