Java ThreadLocal使用

时间:2021-2-26 作者:admin

它是java中的一个为每个线程单独保存数据的类,也就是线程间数据隔离。

1.使用

	 public static void main(String[] args) throws Exception {
	 //定义一个ThreadLocal
	 ThreadLocal<String> local = new ThreadLocal<String>();
	  new Thread(new Runnable() {
		@Override
		public void run() {
			//为当前线程设置一个值
			local.set("thread1");
			try {
				//线程休眠为了等一下其它线程设置值
				TimeUnit.SECONDS.sleep(5);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//打印
			System.out.println("第1个线程值"+local.get());
		}
	}).start();
	 
	 new Thread(new Runnable() {
			@Override
			public void run() {
				local.set("thread2");
				try {
					TimeUnit.SECONDS.sleep(5);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println("第2个线程值"+local.get());
			}
		}).start();
 }

结果:
Java ThreadLocal使用
说明每个线程取到了自己的数据。

2.原理

先看一看ThreadLocal的set方法:

   /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
    	//获取当前线程
        Thread t = Thread.currentThread();
        //获取ThreadLocalMap对象
        ThreadLocalMap map = getMap(t);
        if (map != null)
        	//设置值
            map.set(this, value);
        else
        	//创建ThreadLocalMap对象
            createMap(t, value);
    }

先看一下getMap(t);我们发现getMap(t);取得是当前线程的threadLocals属性也就是说变量实际是存在每个线程自己的属性中的。

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

然后我们看一下ThreadLocalMap是什么类。
他是一个ThreadLocal中的内部类类似Map,他里面也有一个重要的内部类` static class Entry extends WeakReference<ThreadLocal<?>>
注意它是一个弱引用
关于java中引用类型以后有时间再写。

        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
            	//将键设置为弱引用
                super(k);
                value = v;
            }
        }

接下来看一下创建ThreadLocalMap对象 createMap(t, value);

    void createMap(Thread t, T firstValue) {
    	//这个this是当前ThreadLocal对象
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

    ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
         table = new Entry[INITIAL_CAPACITY];
         int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
         //重点是这里将当前ThreadLocal对象设置为弱引用了
         table[i] = new Entry(firstKey, firstValue);
         size = 1;
         setThreshold(INITIAL_CAPACITY);
     }

其实就是创建一个ThreadLocalMap将key,value存进去。
最后get方法就相对简单了

   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对象取出值。

3.内存泄漏问题

ThreadLocalMap的key是当前ThreadLocal为一个弱引用,当我们不想使用ThreadLocal时将它设置为null了,也就是要被垃圾回收器回收了,但是此时我们的ThreadLocalMap生命周期和Thread的一样,它不会回收,这时候就出现了一个现象。那就是ThreadLocalMap的key没了,但是value还在,这就造成了内存泄漏。
所以最好在不使用ThreadLocal时,调用一下remove()方法移除存储的值。

这是我总结的我对ThreadLocal的理解,有不对的地方大家可以讨论。

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。