【并发编程】ThreadLocal学习

目录

ThreadLocal原理

ThreadLocal源码分析

ThreadLocal.get()

ThreadLocal.set()

ThreadLocal.remove()

ThreadLocal内存泄漏问题


ThreadLocal原理

为每一个线程提供独立的变量副本,实现了线程的隔离,保证线程的安全性
spring的@Transactional用到了ThreadLocal机制,使得每个线程保存自己的连接
在service中会调用多个dao,每一个dao都需要调用一次和数据库的connection,用ThreadLocal可以将connection信息绑定在一个线程上,避免重复调用连接

ThreadLocal源码分析

在Thread类下有一个ThreadLocalMap容器,其中有多个Entry[]的map数组,k指向ThreadLocal,v存放ThreadLocal中的数据

THreadLocal .get() .set()的公共代码
Thread t = Thread.currentTHread(); //绑定当前thread
THreadLocalMap map = getMap(t); //生成local副本

ThreadLocal.get()

public T get() {
    Thread var1 = Thread.currentThread();
    ThreadLocal.ThreadLocalMap var2 = this.getMap(var1);
    if (var2 != null) {
        ThreadLocal.ThreadLocalMap.Entry var3 = var2.getEntry(this); //根据Entry[]中的k获取v
        if (var3 != null) {
            Object var4 = var3.value;
            return var4;
        }
    }

    return this.setInitialValue();
}

private ThreadLocal.ThreadLocalMap.Entry getEntry(ThreadLocal<?> var1) {
    int var2 = var1.threadLocalHashCode & this.table.length - 1;
    ThreadLocal.ThreadLocalMap.Entry var3 = this.table[var2];
    return var3 != null && var3.get() == var1 ? var3 : this.getEntryAfterMiss(var1, var2, var3);
}

//扫描Entry[],若存在k==null&&v!=null,回收该Entry
private ThreadLocal.ThreadLocalMap.Entry getEntryAfterMiss(ThreadLocal<?> var1, int var2, ThreadLocal.ThreadLocalMap.Entry var3) {
    ThreadLocal.ThreadLocalMap.Entry[] var4 = this.table;

    for(int var5 = var4.length; var3 != null; var3 = var4[var2]) {
        ThreadLocal var6 = (ThreadLocal)var3.get();
        if (var6 == var1) {
            return var3;
        }

        if (var6 == null) {
            this.expungeStaleEntry(var2);
        } else {
            var2 = nextIndex(var2, var5);
        }
    }

    return null;
}

ThreadLocal.set()

public void set(T var1) {
    Thread var2 = Thread.currentThread();
    ThreadLocal.ThreadLocalMap var3 = this.getMap(var2);
    //如果k存在,则更新对应k的v;不存在则新建kv键值对
    if (var3 != null) {
        var3.set(this, var1);
    } else {
        this.createMap(var2, var1);
    }

}

private void set(ThreadLocal<?> var1, Object var2) {
    ThreadLocal.ThreadLocalMap.Entry[] var3 = this.table;
    int var4 = var3.length;
    int var5 = var1.threadLocalHashCode & var4 - 1;

    for(ThreadLocal.ThreadLocalMap.Entry var6 = var3[var5]; var6 != null; var6 = var3[var5 = nextIndex(var5, var4)]) {
        ThreadLocal var7 = (ThreadLocal)var6.get();
        if (var7 == var1) {
            var6.value = var2;
            return;
        }

        if (var7 == null) {
            this.replaceStaleEntry(var1, var2, var5); //生成新的键值对&回收空k的Entry
            return;
        }
    }
    
    //若hash冲突,重新hash寻找空地址
    var3[var5] = new ThreadLocal.ThreadLocalMap.Entry(var1, var2);
    int var8 = ++this.size;
    if (!this.cleanSomeSlots(var5, var8) && var8 >= this.threshold) {
        this.rehash();
    }

}

private void replaceStaleEntry(ThreadLocal<?> var1, Object var2, int var3) {
    ThreadLocal.ThreadLocalMap.Entry[] var4 = this.table;
    int var5 = var4.length;
    int var7 = var3;

    ThreadLocal.ThreadLocalMap.Entry var6;
    int var8;
    for(var8 = prevIndex(var3, var5); (var6 = var4[var8]) != null; var8 = prevIndex(var8, var5)) {
        if (var6.get() == null) {
        var7 = var8;
        }
    }

    for(var8 = nextIndex(var3, var5); (var6 = var4[var8]) != null; var8 = nextIndex(var8, var5)) {
        ThreadLocal var9 = (ThreadLocal)var6.get();
        if (var9 == var1) {
            var6.value = var2;
            var4[var8] = var4[var3];
            var4[var3] = var6;
            if (var7 == var3) {
                var7 = var8;
            }

            this.cleanSomeSlots(this.expungeStaleEntry(var7), var5); //回收空k的Entry
            return;
        }

        if (var9 == null && var7 == var3) {
            var7 = var8;
            }
    }

    var4[var3].value = null;
    var4[var3] = new ThreadLocal.ThreadLocalMap.Entry(var1, var2);
    if (var7 != var3) {
        this.cleanSomeSlots(this.expungeStaleEntry(var7), var5);
    }

}

ThreadLocal.remove()

public void remove() {
    ThreadLocal.ThreadLocalMap var1 = this.getMap(Thread.currentThread());
    if (var1 != null) {
        var1.remove(this);
    }

}

private void remove(ThreadLocal<?> var1) {
    ThreadLocal.ThreadLocalMap.Entry[] var2 = this.table;
    int var3 = var2.length;
    int var4 = var1.threadLocalHashCode & var3 - 1;

    //清空Entry[]
    for(ThreadLocal.ThreadLocalMap.Entry var5 = var2[var4]; var5 != null; var5 = var2[var4 = nextIndex(var4, var3)]) {
        if (var5.get() == var1) {
            var5.clear();
            this.expungeStaleEntry(var4);
            return;
        }
    }

}

ThreadLocal内存泄漏问题

在调用ThreadLocal类时,会占用大量内存
因为Enry类继承了WeakReference(弱引用),在调用完ThreadLocal后,弱引用被回收,但Entry[]是存放在Thread本身的Map中的,它的内存空间依旧存在,因此若不调用ThreadLocal.remove()手动回收Entry,就只能在Thread结束时清掉Entry,容易造成内存泄漏

不能使用强引用,因为使用强引用会导致它无法被回收,使得内存一定泄露;使用弱引用,在Thread还存在的情况下,可以通过get和set的调用回收掉一部分key空的Entry

虽然ThreadLocal的.get() .set() .remove()都会调用expungeStaleEntry的Entry清除函数,但get和set只有在key为空时才会调用,清除的不够彻底

四种引用
Object o = new Object();
引用定义:在执行上述命令时,new出来的Object是存放在堆中的,那个变量名o则是存放在栈中,并指向堆中的Object实例
强引用
当栈中有一个引用指向了堆实例,该引用被称为强引用,该实例即使内存溢出也不会被回收

软引用 SoftRefence
当内存即将发生溢出时,该引用指向的实例被回收

弱引用 WakeRefence
一旦被垃圾回收器发现具有弱引用的对象,无论内存空间足够与否,都会被回收;但垃圾回收器的优先级较低,不一定能快速地扫描到弱引用实例,因此也具有一定的生命周期

虚引用
没有任何用处,主要用来跟踪对象被垃圾回收的活动

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值