为何要使用ThreadLocal?

一、ThreadLocal作用

1. 线程间隔离,每个线程都拥有自己独立的对象

2. 在线程生命周期内可以获取到该对象

 

二、ThreadLocal优点

1. 线程安全

2. 不需要加锁,提高执行效率

3. 高效利用内存,节省开销

4. 避免繁琐的传参

 

三、两种使用方式:

1. 创建ThreadLocal时,可以控制的对象

    ThreadLocal<SimpleDateFormat> threadLocal2 = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"))

2.  创建ThreadLocal时,没法控制对象

    ThreadLocal<User> threadLocal2 = new ThreadLocal<>();

    threadLocal2.set(user);

 

四、源码

    涉及到的相关类:Thread,ThreadLocal,ThreadLocalMap

 

ThreadLocal.java

1. T initialValue()

    默认实现返回空

    a. 该方法会返回当前线程对应的初始值,这是一个延迟加载的方法,只有在调用get()的时候,才会触发

    b. 当线程第一次调用get()方法访问变量时,将调用此方法,除非线程先调用了set方法,在这种情况下,不会为线程调用本initialValue方法。即:如果先调用了set()方法,则initialValue方法将不会再被调用

2. void set(T t)

    为线程设置一个新值

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            map.set(this, value);
        } else {
            createMap(t, value);
        }
    }

3. T get()

    得到线程对应的value,如果是首次调用get(),则会调用initialize来得到这个值.

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
  • 获取当前线程对应的成员变量ThreadLocalMap
  • 获取当前ThreadLocal对应的entry
  • 获取entry中的value

4. void remove()

    删除线程对应的value

     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null) {
             m.remove(this);
         }
     }

    备注:map中的key和value都是保存在线程中的,而不是保存在ThreadLocal中

 

ThreadLocalMap.java

    每个线程Thread类里面的成员变量,最重要的一个属性 Entry[] table,可以认为是一个map,键值对:

  • 键:这个ThreadLocal
  • 值:实际需要的变量,比如user或者simpleDateFormat对象
	static class Entry extends WeakReference<ThreadLocal<?>> {
		/** The value associated with this ThreadLocal. */
		Object value;

		Entry(ThreadLocal<?> k, Object v) {
			super(k);
			value = v;
		}
	}

六、使用ThreadLocal注意事项

    1. 内存泄漏 OOM

  • ThreadLocalMap的每个Entry都是一个对key的弱引用,同时,每个Entry都包含一个对value的强引用
  • 正常情况下,当线程终止,保存在ThreadLocal里的value会被gc回收,因为没有任何强引用
  • 若线程不终止,那么key对应的value就不能被回收,因为有引用关系: Thread->ThreadLocalMap->Entry(key为null)-> Value
  • jdk保护机制,在set,remove,rehash方法中会扫描key为null的entry,并把对应的value设置为null

    2. 调用remove避免内存泄漏

 

 

 

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
在多线程环境中,我们可以使用ThreadLocal类来实现线程局部变量。ThreadLocal为每个线程提供了一个独立的存储空间,每个线程都可以独立地修改自己的副本,互不干扰。 要在多线程环境中使用ThreadLocal,我们需要执行以下步骤: 1. 创建ThreadLocal对象:通过创建ThreadLocal对象来保存线程的局部变量。例如,可以使用`ThreadLocal<Integer>`来保存一个整数类型的线程局部变量。 2. 设置线程局部变量:在每个线程中,通过调用ThreadLocal对象的`set()`方法来设置该线程的局部变量的值。例如,使用`threadLocal.set(value)`来设置线程局部变量的值为value。 3. 获取线程局部变量:在每个线程中,通过调用ThreadLocal对象的`get()`方法来获取该线程的局部变量的值。例如,使用`threadLocal.get()`来获取线程局部变量的值。 4. 移除线程局部变量(可选):如果需要,在每个线程结束时,可以通过调用ThreadLocal对象的`remove()`方法来移除该线程的局部变量。例如,使用`threadLocal.remove()`来移除线程局部变量。 下面是一个示例代码,演示如何在多线程环境中使用ThreadLocal: ```java public class ThreadLocalExample { private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>(); public static void main(String[] args) { Thread thread1 = new Thread(() -> { threadLocal.set(1); System.out.println("Thread 1: " + threadLocal.get()); threadLocal.remove(); }); Thread thread2 = new Thread(() -> { threadLocal.set(2); System.out.println("Thread 2: " + threadLocal.get()); threadLocal.remove(); }); thread1.start(); thread2.start(); } } ``` 这个示例中,我们创建了一个ThreadLocal对象来保存整数类型的线程局部变量。在每个线程中,我们使用`threadLocal.set()`方法来设置线程局部变量的值,并使用`threadLocal.get()`方法来获取线程局部变量的值。最后,我们调用`threadLocal.remove()`方法来移除线程局部变量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值