Java ThreadLocal

  • ThreadLocal
 /*
 * @since   1.2
 */
public class ThreadLocal<T> {
	private final int threadLocalHashCode = nextHashCode();
    private static AtomicInteger nextHashCode = new AtomicInteger();
    private static final int HASH_INCREMENT = 0x61c88647;
    private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }
	
	ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

	public void set(T value) {
        Thread t = Thread.currentThread();
        // 根据当前线程 获取ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    
	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();
    }
    
	/*
	 * @since 1.5
     */
     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

    /**
     * An extension of ThreadLocal that obtains its initial value from
     * the specified {@code Supplier}.
     */
    static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {

        private final Supplier<? extends T> supplier;

        SuppliedThreadLocal(Supplier<? extends T> supplier) {
            this.supplier = Objects.requireNonNull(supplier);
        }

        @Override
        protected T initialValue() {
            return supplier.get();
        }
    }
	
	static class ThreadLocalMap {
		private static final int INITIAL_CAPACITY = 16;
		//实际上就是Object []
		private Entry[] table;
        //The number of entries in the table
        private int size = 0;
		
		private Entry getEntry(ThreadLocal<?> key) {
			//根据当前ThreadLocal对象作为Key计算出Entry数组中的下标,获取对应的Entry
       		int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            if (e != null && e.get() == key)
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }
        
        //每个Entry中存储了一个Object对象
		static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
	}
	
}

1.在ThreadLocal中,每一个线程在此处只能存储一个Object对象,此Object是该线程在ThreadLocal中独有的数据。所以一般情况下,当我们需要存储多个变量到Thread中,我们需要定义包含包含这些变量的Object,然后直接存储该Object

2.ThreadLocal 内存泄漏的原因
在这里插入图片描述
实心箭头表示强引用,空心箭头表示弱引用

从上图中可以看出,hreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal不存在外部强引用时,Key(ThreadLocal)势必会被GC回收,这样就会导致ThreadLocalMap中key为null, 而value还存在着强引用,只有thead线程退出以后,value的强引用链条才会断掉。

但如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:

Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value
永远无法回收,造成内存泄漏。

广义并通俗的说,就是:不再会被使用的对象或者变量占用的内存不能被回收,就是内存泄露

  • 测试案例
public class MyThreadLocal {

    public static void main(String[] args) {

        for (int i = 0; i < 2; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    //不使用ThreadLocal
                    Pocket instance = Pocket.getInstance();
                    //使用ThreadLocal
                    //Pocket instance = Pocket.getInstance2();
                    int randomData = new Random().nextInt(1000);
                    instance.setName("Test");
                    instance.setNum(new Integer(randomData));
                    System.out.println(randomData);
                    
                    new ModuleA().getData(instance);
                    new ModuleB().getData(instance);
                }
            }).start();
        }
    }

    static class ModuleA {
        public void getData(Pocket instance) {
            System.out.println("Module A: getData from " + Thread.currentThread().getName()+" Name: "+ instance.getName() + " Num: " + instance.getNum().intValue());
        }

    }

    static class ModuleB {
        public void getData(Pocket instance) {
            System.out.println("Module B: getData from " + Thread.currentThread().getName()+" Name: "+ instance.getName() + " Num: " + instance.getNum().intValue());
        }
    }
}

class Pocket {
    
      //不使用 ThreadLocal
    private static Pocket instance;
    public static Pocket getInstance() {
        if(instance == null) {
            instance = new Pocket();
        }
        return instance ;
    }

    //使用ThreadLocal
    // the shared variable in one Thread
    private static ThreadLocal<Pocket> mySoldier = new ThreadLocal<Pocket>();
    public static Pocket getInstance2() {
        Pocket instance = mySoldier.get();
        if (instance == null) {
            instance = new Pocket();
            mySoldier.set(instance);
        }
        return instance;
    }

    private String name;
    private Integer num;
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getNum() {
        return num;
    }
    public void setNum(Integer num) {
        this.num = num;
    }
}

在这里插入图片描述

不使用ThreadLocal,出现因为多线程导致的数据异常
在这里插入图片描述
使用ThreadLocal,各个线程只会读取自己在ThreadLocal中所存储对应的Object数据,不会出现数据异常

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值