美文网首页
十七 线程 ThreadLocal

十七 线程 ThreadLocal

作者: BeYearn | 来源:发表于2018-11-22 20:20 被阅读0次
    1. 线程 : 简单认为,线程是系统调度的最小单元,一个进程可以包含多个线程,作为任务的真正运作者,有自己的栈(Stack)、寄存器(Register)、本地存储(Thread Local)等,但是会和进程内其他线程共享文件描述符、虚拟地址空间等。具体实现中线程还分为内核线程, 用户线程(java的线程实现与虚拟机相关) 自jdk1.2后, 现在的模型是一对一映射到操作系统内核线程

    线程的生命周期的几个阶段


    图片.png

    实现Runnable比继承Thread使用除了避免java不支持多继承, 还能更好的与并发库中Executor之类框架结合使用

    1. threadlocal
      这是java提供的一种保存线程私有信息的机制, 因为其在整个线程生命周期内有效. 所以可以方便的在一个线程关联的不同业务模块之间传递信息, 比如事务ID, Cookies等上下文相关信息

    ThreadLocal里类型的变量,其实是放入了当前Thread里。因为每个Thread都有一个threadLocals字段,它是一个map(ThreadLocalMap)。这个map的entry是ThreadLocalMap.Entry,具体的key就是这个ThreadLocal的弱引用WeakReference<ThreadLocal<?>> ,value是Object。 当new或者withInitial(Supplier)一个ThreadLocal时那个map还未初始化, 只有set时才初始化。
    往进set时,看该Thread的那个字段是否已经初始化了map,有的话就继续往进放。

    private static final ThreadLocal<Integer> TL_INT = ThreadLocal.withInitial(() -> 6);
        private static final ThreadLocal<String> TL_STRING = ThreadLocal.withInitial(() -> "Hello, world");
        private static final ThreadLocal<String> TL_String_new = new ThreadLocal<>();
    
        public static void main(String[] args) throws InterruptedException {
            System.out.println(TL_String_new.get()); //null
            TL_INT.set(TL_INT.get() + 1);
            TL_String_new.set("aaa");
    
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(TL_INT.get()); //6  如果该线程没设置,就得到初始设置的值
                    System.out.println(TL_String_new.get());//null
                }
            });
            thread.start();
            thread.join();
    
            System.out.println(TL_String_new.get()); //aaa
            TL_String_new.remove();
            System.out.println(TL_String_new.get()); //null
            System.out.println(TL_INT.get()); // 7
            TL_INT.set(TL_INT.get() + 1);
            System.out.println(TL_INT.get()); //8
            TL_INT.remove();
            System.out.println(TL_INT.get());// 会重新初始化该value,6
        }
    
    

    其数据存储于线程相关的ThreadLocalMap中, 条目的key使用的是threadlocal对象的弱引用,why:

    • key 使用强引用:使用除new或withInitial引用的ThreadLocal的对象被回收了,但是ThreadLocalMap中(的条目entry)还持有ThreadLocal的强引用,如果没有手动删除,此时ThreadLocal对象不会被回收,导致Entry内存泄漏。
    • key 使用弱引用:引用的ThreadLocal的对象被回收了,由于ThreadLocalMap持有ThreadLocal的弱引用,即使没有手动删除,ThreadLocal在一次gc后也会被回收,这时候ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value,如果当前线程再迟迟不结束的话(ThreadLocalMap的生命周期跟Thread一样长),好在ThreadLocal的get(),set(),remove()的时候都会清除线程ThreadLocalMap里所有key为null的value。
    private void set(ThreadLocal<?> key, Object value) {
        Entry[] tab = table;
        int len = tab.length;
        int i = key.threadLocalHashCode & (len-1);
    
        for (Entry e = tab[i];; …) {
            //…
            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,都调用它的remove()方法,清除数据。

    注意一个地方. 通常弱引用都会和引用队列配合清理机制使用, 一般不建议与线程池配合, 因为worker线程往往不会退出的

    相关文章

      网友评论

          本文标题:十七 线程 ThreadLocal

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