十七 线程 ThreadLocal

2018-11-22  本文已影响0人  BeYearn
  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:

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线程往往不会退出的

上一篇下一篇

猜你喜欢

热点阅读