美文网首页
ThreadLocal

ThreadLocal

作者: 盼旺 | 来源:发表于2019-09-29 10:14 被阅读0次

理解

线程的本地变量,赋值之后,多个线程之间的值不可见。 结合线程池使用时线程结束时要清除本次使用的本地变量。
整体:每个线程内部维护了一个ThreadLocal.ThreadLocalMap的变量threadLocals(key:ThreadLocal,value),ThreadLocal赋值取值时操作的其实都是该变量,该变量是每个线程的私有变量,必然不同线程之间不可见。

定义初始化

 ThreadLocal<Integer> threadLocal = new ThreadLocal<>();

threadLocal.set()方法

set方法中,首先获取当前线程ThreadCurrent.currentThread(),然后getMap(t)获取获取线程的ThreadLocal.ThreadLocalMap变量,不为空,set值的时候,key为当前线程的TheadLocal实例,value为传入的value值,为空创建一个空的ThreadCurrent.currentThread()并赋值。

getMap方法
createMap方法

threadLocal.get()方法

注意没有传入的参数,因为key默认是当前线程的TheadLocal实例

setInitialValue方法
可以看到最后返回了一个方法,该方法就是没找到就顺便这次get的时候插入一个空值。initialValue()返回null

threadLocal.remove() 方法

从当前线程中移除以当前ThreadLocal的实例为key的值


应用场景

ThreadLocal是用在多线程的场景的
1.保存线程上下文信息,在任意需要的地方可以获取
2.线程安全的,避免某些情况需要考虑线程安全必须同步带来的性能损失

1.

保存线程上下文信息一般用在优秀的框架里面:比如Spring的事务管理,用ThreadLocal存储Connection,从而各个DAO可以获取同一Connection,可以进行事务回滚,提交等操作。

2.


由于不需要共享信息,自然就不存在竞争问题了,从而保证了某些情况下线程的安全,以及避免了某些情况需要考虑线程安全必须同步带来的性能损失!!!
ThreadLocal提供了线程安全的另一种思路,我们平常说的线程安全主要是保证共享数据的并发访问问题,通过sychronized锁等保证数据的一致性。ThreadLocal是让每个线程都拥有一份线程私有的数据,线程之间彼此不影响。
例子:
public class ThreadLocalTest {
    private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) {
        new Thread(() -> {
            try {
                for (int i = 0; i < 100; i++) {
                    threadLocal.set(i);
                    System.out.println(Thread.currentThread().getName() + "====" + threadLocal.get());
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            } finally {
                threadLocal.remove();
            }
        }, "threadLocal1").start();
        new Thread(() -> {
            try {
                for (int i = 0; i < 100; i++) {
                    System.out.println(Thread.currentThread().getName() + "====" + threadLocal.get());
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            } finally {
                threadLocal.remove();
            }
        }, "threadLocal2").start();
    }
}

threadLocal1进行set值对threadLocal2并没有任何影响!

再看一个例子

public class ThreadLocalDemo {

    private static ThreadLocal<Integer> initVal = new ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
            return 0;//重写上面默认返回null的函数
        }
    };

    public static void modify() {
        initVal.set(initVal.get() + 10);
        System.out.println(initVal.get());
    }


    public static void main(String[] args) {
        new Thread("Thread#1") {
            @Override
            public void run() {
                modify();
            }
        }.start();
        new Thread("Thread#2") {
            @Override
            public void run() {
                modify();
            }
        }.start();
    }
}

产生副本图

回收问题

ThreadLocal被垃圾回收后,在ThreadLocalMap里对应的Entry的键值会变成null,但是Entry是强引用,那么Entry里面存储的Object,并没有办法进行回收,所以ThreadLocalMap 做了一些额外的回收工作。就是需要调用
threadLocal.remove();且最好在finally下调用。

ThreadLocalMap中的Key是ThreadLocal实例对象

ThreadLocalMap中的key是ThreadLocal实例对象,并不是想当然的线程ID等,用线程ID唯一值的话再一个线程有多个ThreadLocal变量时就不知道应该获取哪个Value

ThreadLocal用static修饰

static修饰ThreadLocal的话,使之成为静态变量而不是实例变量,避免了重复创建ThreadLocal实例,因为ThreadLocal本身就是线程的副本
参考文章
https://juejin.im/post/5d707e4fe51d4561e43a6d0d
https://juejin.im/post/5d6c730051882566ce35a887

相关文章

网友评论

      本文标题:ThreadLocal

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