从源码认识ThreadLocal

java的四种引用方式

强引用

等号new出来的都是强引用。只要引用存在,就不会被gc,即使会oom。

软引用

String value = new String("value")
SoftReference sr = new SoftReference(value);

value即为sr的引用对象,通过sr.get()可以得到value对象。
当空间不够用的时候软引用对象会被优先回收,避免出现oom。软引用更多应用在缓存中。

弱引用

String value = new String("value");
WeakReference wr = new WeakReference(value);

弱引用相比于软引用来说更弱,也可以通过get方法获得引用对象。在垃圾回收器发现弱引用对象时就会将其回收。

虚引用

String value = new String("value");
Queue QUEUE ;
PhantomReference pr = new PhantomReference(value, (ReferenceQueue) QUEUE);

虚引用是get不到引用对象的。
所有new出来的对象都在堆内存当中,堆内存之外有一块空间叫做直接内存(堆外内存)。直接内存是不归gc管理的。
虚引用引用的对象会存放在直接内存中,管理直接内存。在虚引用对象被回收时,会被放进pr new时的队列中,队列传递消息,查看直接内存中有没有当前虚引用对象的引用对象,如果有jvm将其回收。

ThreadLocal

定义

源码注释

This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its {@code get} or {@code set} method) has its own, independently initialized copy of the variable. {@code ThreadLocal} instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).

大致翻译就是此类提供线程局部变量。这些变量与正常变量不同,因为每访问一个线程(通过其{@code get}或{@code set}方法)的线程都有其自己独立初始化的变量副本。 {@code ThreadLocal}实例通常是希望将状态与线程相关联的类中的私有静态字段(例如用户ID或交易ID)。

示例代码

class Demo{
    public static void main(String[] args) {
        ThreadLocal threadLocal = new ThreadLocal();
        new Thread(()->{
            threadLocal.set("thread1");
            System.out.println("线程1");
        }).start();
        new Thread(()->{
            Object o = threadLocal.get();
            System.out.println("线程2");
            System.out.println("threadlocal"+o);
        }).start();
    }
}

执行结果

线程1
线程2
threadlocalnull

从示例代码中可以看到,threadlocal通过set方法存放局部变量。

查看set、get方法
public void set(T value) {
    Thread t = Thread.currentThread();
    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();
}

我们发现,threadlocal中使用了ThreadLocalMap存储本地变量副本信息,即为key-value的形式。以当前线程的ThreadLocal对象为key,存放对应的value。
set方法:首先查看当前线程是否已经有ThreadLocalMap,如果有则使用没有则创建。
get方法:查看当前线程是否已经有ThreadLocalMap,如果有则获取对应的value,没有则 setInitialValue() 初始化。

查看ThreadLocalMap的set方法
private void set(ThreadLocal<?> key, Object value) {
    // We don't use a fast path as with get() because it is at
    // least as common to use set() to create new entries as
    // it is to replace existing ones, in which case, a fast
    // path would fail more often than not.
    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)]) {
        ThreadLocal<?> k = e.get();
        if (k == key) {
            e.value = value;
            return;
        }
        if (k == null) {
            replaceStaleEntry(key, value, i);
            return;
        }
    }
    tab[i] = new Entry(key, value);
    int sz = ++size;
    if (!cleanSomeSlots(i, sz) && sz >= threshold)
        rehash();
}

我们会发现是会将value存放在Entry这个类的数组当中。

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

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

Entry继承了WeakReference,以此种方式避免造成内存泄漏。

为什么使用弱引用指向就会避免造成内存泄漏?
threadLocal 引用ThreadLocal,key引用ThreadLocal,如果threadLocal 是null,key仍然引用ThreadLocal,key是强引用的情况下,ThreadLocal就不会被回收。
注意:当threadLocal 不用时要调用threadLocal.remove(),如果不remove,key为null时,无法找到对应的value,value所在的这一块空间就无法回收。
【threadLocal 当前线程的ThreadLocal】
为什么ThreadLocalMap要使用Entry数组实现?
因为一个线程是可以有多个ThreadLocal对象的,维护一个ThreadLocalMap。Entry是用当前ThreadLocal对象作为key,多个ThreadLocal就是多个key,所以使用数组实现,同时也需要考虑到数组扩容问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值