java多线程---TheadLocal分析

ThreadLocal 分析

ThreadLocal 作用

ThreadLocal 可以看做是一个线程的副本,每个线程中的ThreadLocal都是不一样的。访问ThreadLocal就是在访问只属于线程自己的变量。那么,这个是怎么做到,每个线程都有自己的ThreadLocal?看下面2个方法
set

 public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, 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 getMap(Thread t) {
        return t.threadLocals;
}

从上面2个方法中,可以很清楚的看到,对于threadlocal的get和set都是对当前线程中,一个threadlocalmap的变量修改。所以,我们在使用的时候,会发现每个线程中的threadlocal都是独立的,只属于当前线程。
例如:

public class ThreadLocalDemo {

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


    public static void main(String[] args) throws InterruptedException {

        ThreadPoolExecutor executor = new ThreadPoolExecutor(5,10,100,TimeUnit.SECONDS,new LinkedBlockingDeque<>());

        for(int  i = 0;i<10;i++){
            executor.submit(() ->{
                Thread thread =  Thread.currentThread();
                threadLocal.set(thread.getName());
                System.out.println(threadLocal.get());
            });
        }

        executor.shutdown();
    }


}

得到结果为

pool-1-thread-3
pool-1-thread-5
pool-1-thread-4
pool-1-thread-2
pool-1-thread-1
pool-1-thread-2
pool-1-thread-4
pool-1-thread-5
pool-1-thread-3
pool-1-thread-1

ThreadLocal缺点

其实ThreadLocal,在使用过程中,如果使用不正确,会造成内存泄漏。

ThreadLocalMap 中属性如下

private Entry[] table;
private int size = 0;
private int threshold;

 static class Entry extends WeakReference<ThreadLocal<?>> {
   //在threadlocal中value值
    Object value;

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

可以看到一个很关键的类WeakReference,于是threadlocal中的value存在在entry中的value中,而ThreadLocalMap中的key,就是referent变量。属于弱引用。所以,当进行gc时,并且存在地方对threadlocal的引用,会存在把key回收了,但是value没有回收这样的情况。这样会造成内存的泄漏。

为了解决这一问题,threadlocal中提供了一个remove方法。如下

private void remove(ThreadLocal<?> key) {

    Entry[] tab = table;
    int len = tab.length;
    //这里和hashmap中的分配桶,非常相似
    int i = key.threadLocalHashCode & (len-1);
    //如果i的定位不够准确,再去遍历数组
    for (Entry e = tab[i];
         e != null;
         e = tab[i = nextIndex(i, len)]) {
        if (e.get() == key) {
        //使用weakreference的clear方法,清除弱引用
            e.clear();
           //消除,key为null的元素
           expungeStaleEntry(i);
            return;
        }
    }
}

总结

ThreadLocal这里如果设计成强引用,那么,及时没有其他地方对它引用,它也不会被回收。而弱引用会被回收,只需要在使用完毕后,及时的调用remove去解决内存泄漏,便不存在问题。

引用类型

介绍一下在java中的四大引用类型如下:

  1. 强引用(StrongReference)

回收时机,强引用不会被GC回收,当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。

2.软引用(SoftReference)

回收时机,如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存,但也不一定会回收全部的软引用,更倾向于回收那些在内存中停留时间比较久的软引用
3. 弱引用(WeakReference)

回收时机,只要 GC 发现一个对象只有弱引用,则就会回收此弱引用对象。但是由于GC所在的线程优先级比较低,不会立即发现所有弱引用对象并进行回收。只要GC对它所管辖的内存区域进行扫描时发现了弱引用对象就进行回收

4.虚引用

虚引用并不会影响对象的生命周期。虚引用的作用为:跟踪垃圾回收器收集对象这一活动的情况。
当GC一旦发现了虚引用对象,则会将PhantomReference对象插入ReferenceQueue队列,而此时PhantomReference对象并没有被垃圾回收器回收,而是要等到ReferenceQueue被你真正的处理后才会被回收。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值