juc之五: ThreadLocal

1. ThreadLocal (JDK版本)

1.1 ThreadLocal 概述

juc之五: ThreadLocal_第1张图片
ThreadLocal方法及内部类.png

1.2 Thread-ThreadLocal-ThreadLocalMap 关系

Thread类有成员变量threadLocals (类型是ThreadLocal.ThreadLocalMap),
也就是说每个线程有一个自己的ThreadLocalMap ,
所以每个线程往这个ThreadLocal中读写隔离的,并且是互相不会影响的。

一个ThreadLocal只能存储一个Object对象,
如果需要存储多个Object对象那么就需要多个ThreadLocal!
juc之五: ThreadLocal_第2张图片
Thread-ThreadLocal-ThreadLocalMap.jpg

1.2.1 ThreadLocalMap

java对象的引用包括 :
强引用,软引用,弱引用,虚引用 。

因为这里涉及到弱引用,简单说明下:
弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,
无论内存是否充足,该对象仅仅被弱引用关联,那么就会被回收。

当仅有ThreadLocalMap中的Entry的key指向ThreadLocal的时候,ThreadLocal会进行回收的!

ThreadLocal被垃圾回收后,在ThreadLocalMap里对应的Entry的键值会变成null,
但是Entry是强引用,那么Entry里面存储的Object,并没有办法进行回收,
所以ThreadLocalMap 做了一些额外的回收工作。
!!!虽然做了但是也会存在内存泄漏风险
juc之五: ThreadLocal_第3张图片
ThreadLocalMap.png
juc之五: ThreadLocal_第4张图片
ThreadLocal防止内存泄漏做的工作-->不够.jpg

1.3 ThreadLocal的最佳实践(可以参考阿里巴巴java规范)

ThreadLocal被垃圾回收后,在ThreadLocalMap里对应的Entry的键值会变成null,
但是Entry是强引用,那么Entry里面存储的Object,并没有办法进行回收,所以ThreadLocalMap 做了一些额外的回收工作。
#备注:
很多时候,我们都是用在线程池的场景,程序不停止,线程基本不会销毁!!!

由于线程的生命周期很长,如果我们往ThreadLocal里面set了很大很大的Object对象,
虽然set、get等等方法在特定的条件会调用进行额外的清理,
但是ThreadLocal被垃圾回收后,在ThreadLocalMap里对应的Entry的键值会变成null,
但是后续再也没有操作set、get等方法了。

#所以最佳实践
应该在我们不使用的时候,主动调用remove方法进行清理。
try {
    // 其它业务逻辑
} finally {
    threadLocal对象.remove();
}
juc之五: ThreadLocal_第5张图片
ThreadLocal最佳实践.jpg

1.4 ThreadLocal用在什么地方?

#应用场景概述
>> 保存线程上下文信息,在任意需要的地方可以获取!!!
>> 线程安全的,避免某些情况需要考虑线程安全必须同步带来的性能损失!

#例如
由于ThreadLocal的特性,同一线程在某地方进行设置,在随后的任意地方都可以获取到。
从而可以用来保存线程上下文信息。

常用的比如每个请求怎么把一串后续关联起来,就可以用ThreadLocal进行set,
在后续的任意需要记录日志的方法里面进行get获取到请求id,从而把整个请求串起来。

还有比如Spring的事务管理,用ThreadLocal存储Connection,
从而各个DAO可以获取同一Connection,可以进行事务回滚,提交等操作。

1.5 ThreadLocal应用示例

1.5.1 用于存储线程信息

package com.zy.tools.undefined.concurrent.threadlocal;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ThreadLocalDemo01 {
    private static final ExecutorService executor = Executors.newCachedThreadPool();
    private static ThreadLocal threadLocal = new ThreadLocal<>();
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(4);
        for (int i = 1; i < 5; i++) {
            int finalI = i;
            executor.submit(() -> {
                try {
                    threadLocal.set(Thread.currentThread().getName() + " ---> " + finalI);
                    System.out.println(threadLocal.get());
                    countDownLatch.countDown();
                } finally {
                    threadLocal.remove();
                }
            });
        }
        countDownLatch.await(5L, TimeUnit.SECONDS);
        executor.shutdown();
    }
}

2. FastThreadLocal (Netty版本)

参考资源
https://mp.weixin.qq.com/s/yBLIbWs7bA0rjYj3Ypxd9g (JDK ThreadLocal)

你可能感兴趣的:(juc之五: ThreadLocal)