ThreadLocal学习

前言

基于引用的学习,来简单的看一下ThreadLocal的实现。ThreadLocal简单来时是线程本地变量,解决共享对象(单个线程内共享)的多线程访问问题的,其不同于synchonized的关键点在于,synchronized是利用锁机制,保证共享对象在某一时刻只能被一个线程访问,但是ThreadLocal是为每个线程提供变量副本。其劣势在于为每个线程并发访问的数据建立了一个副本,会造成内存消耗。
常见的坑:没有进行remove操作,会造成内存泄露问题。(注意并不是ThreadLocal出现的地方就得调用remove,如果ThreadLocal是静态变量等情况,其生命周期和程序一样长,就不是很必要了)

内存泄露原因分析

ThreadLocalMap

static class ThreadLocalMap {
	// 继承自弱引用意味着在发生gc时,如果key值ThreadLocal被发现只有一个弱引用,其会被回收,此时如果发现创建ThreadLocal的线程一直在运行的话,那么Entry对象的value值将一直得不到回收,发生内存泄漏。
	static class Entry extends WeakReference<ThreadLocal> {
	            /** The value associated with this ThreadLocal. */
	            Object value;
			  /**
				* public WeakReference(T referent) {
        		*	super(referent);
    			* }
				*/
	            Entry(ThreadLocal k, Object v) {
	                super(k);
	                value = v;
	            }
	        }
	  ...
	  // 由set方法中createMap调用
	   ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }
      ...
	 // 由map方法中map.getEntry(this)调用
	  private Entry getEntry(ThreadLocal key) {
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            if (e != null && e.get() == key)
                return e;
            else
            	// 此过程中会清理key值为空的Entry对象
                return getEntryAfterMiss(key, i, e);
        }
}

set方法

    public void set(T value) {
    	// t为正在执行的当先线程的句柄
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
        	// 此过程也会清理key值为空的Entry对象
            map.set(this, value);
        else
            createMap(t, value);
    }

     void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
	/**
	* ThreadLocal.ThreadLocalMap threadLocals = null;
	*/
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

get方法

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }
   

简单示例

public class ThreadLocalTest {
    public static void main(String[] args) {
        ThreadLocal<String> localName = new ThreadLocal<>();
        try {
            localName.set("Hello World");
            String name = localName.get();
            System.out.println(name);
        } finally {
        	// 此处请注意
            localName.remove();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ThreadLocal是一种特殊的变量存储类,它允许每个线程拥有自己的副本变量,从而避免了线程之间的共享变量冲突。在Java中,ThreadLocal通常用于存储线程局部数据,即每个线程都有自己的数据副本,而不会受到其他线程的影响。 要使用ThreadLocal,首先需要创建一个ThreadLocal对象,并使用其set()方法将数据存储在当前线程的副本中。例如: ```java ThreadLocal<Integer> threadLocal = new ThreadLocal<>(); threadLocal.set(42); ``` 在这个例子中,我们创建了一个ThreadLocal对象threadLocal,并将其初始化为空值。然后,我们使用set()方法将整数值42存储在当前线程的副本中。 接下来,我们可以通过get()方法从当前线程中获取存储在该ThreadLocal对象中的值。例如: ```java int value = threadLocal.get(); ``` 这个例子中,我们从当前线程的副本中获取了存储在threadLocal中的值,并将其存储在变量value中。由于每个线程都有自己的副本,因此我们可以通过这种方式在不同的线程之间传递数据。 需要注意的是,ThreadLocal中的数据存储在每个线程的本地内存中,因此如果一个线程修改了存储在ThreadLocal中的值,它不会影响其他线程中的副本。这意味着ThreadLocal通常用于存储需要在多个线程之间隔离的数据。 除了set()和get()方法外,ThreadLocal还提供了remove()方法来删除当前线程中的数据副本。此外,还可以使用getAndSet()方法来获取当前线程中的值并设置新的值。 总之,ThreadLocal是一种非常有用的工具,它允许每个线程拥有自己的数据副本,从而避免了共享变量之间的冲突。它通常用于需要隔离不同线程之间的数据的情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值