ThreadLocal分析

1.线程本地变量,使用方式

public class ThreadLocalDemo {
    public static class ThreadLocalWrapper {
        static ThreadLocal<Integer> count = new ThreadLocal<>();
    }
    public static class ThreadLocalSet{
        private void setCount() {
            ThreadLocalWrapper.count.set(1);
        }
    }
    public static class ThreadLocalPrint{
        private static void printCount() {
            System.out.println("count:"+ThreadLocalWrapper.count.get());
        }
    }
    public static void main(String[] args) {
        ThreadLocalSet set = new ThreadLocalSet();
        set.setCount();
        ThreadLocalPrint.printCount();
    }
}

以上代码输出:  count:1,实现了隐式传参。

 

2.线程本地变量创建过程

2.1.实例化时根据hash种子自增方式创建hashCode

2.2.线程中赋值时,线程中如果threadLocals为空则 threadLocals = new ThreadLocalMap(this,value);其中this指当前的TreadLocal对象

2.2.1ThreadLocalMap的set操作会创建一个entry,entry中的key被定义为弱引用,再根据threadLocal对象中的hashCode算出其在hash桶中的位置并放置在该位置,如发现该位置有其他ThreadLocal则位置+1。

2.3.删除过程即将entry删除

 

3.hash寻址与HashMap的区别,解决哈希冲突的算法不一样。HashMap使用的是链地址法。ThreadLocalMap使用的是开放寻址法。

 

4.怎样理解使用弱引用是为解决内存泄露。为此需要将以上代码改一下:

 

public class ThreadLocalDemo {
    public static class ThreadLocalWrapper {
        static ThreadLocal<Integer> count = new ThreadLocal<>();
    }
    public static class ThreadLocalSet{
        private void setCount() {
            //每次使用前创建一个新的ThreadLocal
            ThreadLocalWrapper.count = new ThreadLocal<>();
            ThreadLocalWrapper.count.set(1);
        }
    }
    public static class ThreadLocalPrint{
        private static void printCount() {
            System.out.println("count:"+ThreadLocalWrapper.count.get());
        }
    }
    public static void main(String[] args) {
        //创建一个只有一个线程的线程池,线程重复使用
        ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>());
        for(int i=1;i<=1000;i++){
            executor.execute(()->{
                ThreadLocalSet set = new ThreadLocalSet();
                set.setCount();
                ThreadLocalPrint.printCount();
                //使用完之后通常做法不会手动做remove操作
                //ThreadLocalWrapper.count.remove();
            });
        }
    }
}

 

执行1000次

如果是threadLocalMap.entry.key是强引用,则在垃圾回收时所有ThreadLocal对象都不能回收。

如果是threadLocalMap.entry.key是弱引用,则只有当前时间点的唯一一个在ThreadLocalWrapper .count静态变量上建立了强引用,其他对象则被回收。

 

更简单举例:

public class ThreadLocalDemo2 {
    public static void main(String[] args) {
        ThreadLocal<Integer> count;
        while(true){
            count = new ThreadLocal<>();
            count.set(1);
            count.get();
        }
    }
}

while每次循环都创建新的TrheadLocal对象,会在当前线程的ThreadLocalMap中插入entry,强引用情况下所有entry都不能释放。弱引用情况下,除当前使用的ThreadLocal之外都只有弱引用。因此可被回收。

 

 

 

 

 使用简单举例代码跑出来的结果,未发现溢出

 

 

 

 

 

 

 entry存在变小的情况,因此也在被回收。

 

posted on 2021-12-21 17:37  宋梦强  阅读(34)  评论(0编辑  收藏  举报