threadlocal

threadlocal

threadlocal为每一个线程单独创建一个变量副本,各个线程之间互不影响,可以在指定线程中存储数据,只有指定线程可以访问到

1,基本操作

public class ThreadLocalTest {

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

    public static class Test implements Runnable{

        private static int id;

        public Test(int id){
            Test.id = id;
        }

        @Override
        public void run() {
            threadLocal.set(id);
            System.out.println("this Thread: " + Thread.currentThread().getName() + "  id: " + threadLocal.get());
        }
    }

    public static void main(String[] args) {
        Test t1 = new Test(11);
        Test t2 = new Test(22);
        Thread thread1 = new Thread(t1);
        thread1.setName("thread1");
        Thread thread2 = new Thread(t2);
        thread2.setName("thread2");
        thread1.start();
        thread2.start();
    }
}

运行结果:

在这里插入图片描述

2,源码分析

threadlocal数据结构:

在这里插入图片描述

ThreadLocal内部有一个静态内部类ThreadLocalMap,该内部类才是实现线程隔离机制的关键,get()、set()、remove()都是基于该内部类操作。

2.1 set()

    public void set(T value) {
        // 获取当前线程
        Thread t = Thread.currentThread();
        // 获取到当前线程对应的ThreadLocalMap
        ThreadLocalMap map = getMap(t);
      	// 如果map不为空则直接将当前threadlocal对象作为key,值作为value
        if (map != null)
            map.set(this, value);
        else
          	// 否则先初始化一个map,在设置k-v
            createMap(t, value);
    }

每一个Thread都有一个ThreadLocalMap

ThreadLocal.ThreadLocalMap threadLocals

ThreadLocalMapThreadLocal的一个内部类,数组实现,每一个元素是一个entry的k-v对象

       private void set(ThreadLocal<?> key, Object value) {
            Entry[] tab = table;  // 就是threadlocalmap
            int len = tab.length; 
            int i = key.threadLocalHashCode & (len-1); // 计算哈希值,得到存放索引

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();

                if (k == key) {
                  	// 该位置为空,直接存入
                    e.value = value;
                    return;
                }

                if (k == null) {
                    // if k == null but v != null 则说明这个key被回收了,因为key是弱饮用,value是强引用
                    // Threadlocal在这里对这种情况进行替换,减少了发生内存泄漏的概率
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            // 清除 key == null 的entry, 可以防止内存泄漏
        		// 若大于阈值则扩容
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

2.2 get()

    public T get() {
        // 同样道理,想获取当前线程
        Thread t = Thread.currentThread();
        // 获取对应的threadlocalmap
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            // 通过当前线程作为key获取对应的entry对象
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

2.3 remove()

     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }
      
		private void remove(ThreadLocal<?> key) {
            // 搜索指定key
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);
            // 线性探测法
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                if (e.get() == key) {
                    // entry的key设置为空
            				e.clear();
            				//处理key==null
                    expungeStaleEntry(i);
                    return;
                }
            }
        }

3,注意事项

  1. ThreadLocal相对于synchronized这种同步方式,ThreadLocal是采用了以空间获取时间来提供更高效的同步方式:每个线程存储一份数据拷贝而不是通过控制对共享资源的访问来实现同步

  2. ThreadLocal为什么会产生内存泄漏?

    • threadlocal设置为null,gc可以回收key,但是value和当前线程有强引用,所以只有当前线程销毁,才会回收value的内存,但是在threadlocal设置为null和线程结束这段时间内,value是不会被回收的,所以就有可能造成内存泄漏。在线程池中,更容易发生内存泄漏。

    • 但是threadlocal内部其实会对key==null的entry进行清除或替换,减少了发生内存泄漏的情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值