zoukankan      html  css  js  c++  java
  • ThreadLocal

    一、ThreadLocal 介绍

    1.1 ThreadLocal 是什么?

    ThreadLocal 叫做线程变量,在 ThreadLocal 中填充的变量属于 当前 线程,该变量对其他线程而言是隔离的。ThreadLocal 为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。

    1.2 ThreadLocal特性

    ThreadLocalSynchronized 都是为了解决多线程中相同变量的访问冲突问题,不同的点是

    • Synchronized 通过线程等待,牺牲时间来解决访问冲突
    • ThreadLocal 是通过每个线程单独一份存储空间,牺牲空间来解决冲突,并且相比于 SynchronizedThreadLocal 具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问到想要的值。

    1.3 使用场景

    • 在进行对象跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束。
    • 线程间数据隔离
    • 进行事务操作,用于存储线程事务信息
    • 数据库连接,Session会话管理

    1.4 ThreadLocal 方法介绍

    // 设置值
    void set(T value);
    // 获取值
    T get();
    // 删除值
    void remove();
    // 设置默认缺省值
    T initialValue();
    

    二、ThreadLocal 的使用

    2.1 实例一: ThreadLocal 返回默认值

    public static void main(String[] args) {
        ThreadLocal<Integer> defaultValueLocal = new ThreadLocal() {
            public Integer initialValue() {
                return 1;
            }
        };
    
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    final Integer result = defaultValueLocal.get();
                    System.out.println(Thread.currentThread().getName()+ " --> result=" + result);
                }
            });
            thread.start();
        }
    }
    

    控制台输出:

    Thread-0 --> result=1
    Thread-1 --> result=1
    Thread-2 --> result=1
    Thread-3 --> result=1
    Thread-4 --> result=1
    Thread-5 --> result=1
    Thread-6 --> result=1
    Thread-7 --> result=1
    Thread-8 --> result=1
    Thread-9 --> result=1
    

    2.2 实例二:set 设置值

    public static void main(String[] args) {
        ThreadLocal<String> nameLocal = new ThreadLocal<>();
    
        for (int i = 0; i < 10; i++) {
            int finalI = i;
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    nameLocal.set(Thread.currentThread().getName() + " -->" + finalI);
                    try {
                        Thread.sleep(300);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("当前线程输出:" + nameLocal.get());
                }
            });
            thread.start();
        }
    }
    

    控制台输出:

    当前线程输出:Thread-2 -->2
    当前线程输出:Thread-1 -->1
    当前线程输出:Thread-0 -->0
    当前线程输出:Thread-3 -->3
    当前线程输出:Thread-9 -->9
    当前线程输出:Thread-8 -->8
    当前线程输出:Thread-7 -->7
    当前线程输出:Thread-6 -->6
    当前线程输出:Thread-5 -->5
    当前线程输出:Thread-4 -->4
    

    三、源码

    3.1 set 方法

    public void set(T value) {
        // 获取当前线程对象
        Thread t = Thread.currentThread();
        // 获取 ThreadLocalMap 对象
        ThreadLocalMap map = getMap(t);
        // 判断 ThreadLocalMap 对象是否存在
        if (map != null)
            // 存在,则设置值到 ThreadLocalMap 中
            map.set(this, value);
        else
            // 否则创建 ThreadLocalMap 对象
            createMap(t, value);
    }
    
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    

    **ThreadLocalMap ** 对象是 ThreadLocal 的一个静态内部类,里面定义了一个 Entry 来保存数据,而且还是继承的弱引用。在 Entry 内部使用 ThreadLocal 作为key,使用我们设置的 value 作为 value:

    static class ThreadLocalMap {
        static class Entry extends WeakReference<ThreadLocal<?>> {       
            Object value;
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
    }	
    

    3.2 get方法

    public T get() {
        // 获取当前线程对象
        Thread t = Thread.currentThread();
        // 获取 ThreadLocalMap 对象
        ThreadLocalMap map = getMap(t);
        // 判断 ThreadLocalMap 对象是否存在
        if (map != null) {
            // 获取当前线程存储变量
            ThreadLocalMap.Entry e = map.getEntry(this);
            // 变量不为空时,将值返回
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        // ThreadLocalMap 对象不存在或不存在指定KEY值时,返回默认值
        return setInitialValue();
    }
    
    // 设置初始默认值
    private T setInitialValue() {
        // 调用初始默认值设置函数
        T value = initialValue();
        // 将初始默认值存储到 ThreadLocalMap 对象中
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
    

    3.3 initialValue 方法

    protected T initialValue() {
        // 默认返回 null,按需覆盖
        return null;
    }
    

    四、ThreadLocal 内存泄漏风险

    在上面我们介绍 ThreadLocal 源码的时候,了解到 ThreadLocal 通过 ThreadLocalMap 对象存储值,它的 key 存储的是当前的 ThreadLocal 的引用,属于弱引用。当 ThreadLocal 被回收时,value 却还存在,就会造成内存泄漏。

    解决办法: 在使用完 ThreadLocal 后,执行 remove 操作,避免出现内存溢出情况。

  • 相关阅读:
    【C++基础】重载,覆盖,隐藏
    【Lintcode】003.Digit Counts
    【C++ Primer 5th】Chapter 15
    【Lintcode】120.Word Ladder
    牛客网上的题
    二叉树中和为某个值得路径
    数据库
    二叉搜索树的后序遍历序列
    从上往下打印二叉树
    二叉树的镜像
  • 原文地址:https://www.cnblogs.com/markLogZhu/p/13852525.html
Copyright © 2011-2022 走看看