threadlocal底层实现_什么是底层

threadlocal底层实现_什么是底层ThreadLocal作用:提供线程内的局部变量,不同的线程之间不会相互干扰,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或组件之间一些公共变量传递的复杂性。package com.mupack;public class App{ private String content; public void setContent(String content) { this.content = content; } public Stri

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

ThreadLocal

作用
提供线程内的局部变量,不同的线程之间不会相互干扰,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或组件之间一些公共变量传递的复杂性。

package com.mupack;

public class App
{ 
   
    private String content;

    public void setContent(String content) { 
   
        this.content = content;
    }
    public String getContent() { 
   
        return content;
    }
    public static void main(String[] args) throws Exception { 
   
        App app = new App();
        for(int i = 0;i < 5; i ++){ 
   
            Thread thread = new Thread(new Runnable(){ 
   

                @Override
                public void run() { 
   
                    app.setContent(Thread.currentThread().getName() + "的数据");
                    System.out.println("--------------------");
                    System.out.println(Thread.currentThread().getName() + "-------->" + app.getContent());
                }
            });
            thread.setName("线程" + i);
            thread.start();
        }
    }
}

在这里插入图片描述


换成ThreadLocal后

package com.mupack;

public class App
{ 
   
    ThreadLocal<String> t1 = new ThreadLocal<>();
    public void setContent(String content) { 
   
        t1.set(content);
    }
    public String getContent() { 
   
        return t1.get();
    }
    public static void main(String[] args) throws Exception { 
   
        App app = new App();
        for(int i = 0;i < 5; i ++){ 
   
            Thread thread = new Thread(new Runnable(){ 
   

                @Override
                public void run() { 
   
                    app.setContent(Thread.currentThread().getName() + "的数据");
                    System.out.println("--------------------");
                    System.out.println(Thread.currentThread().getName() + "-------->" + app.getContent());
                }
            });
            thread.setName("线程" + i);
            thread.start();
        }
    }
}

在这里插入图片描述


ThreadLocal典型应用场景
Mapper层需要处理异常以判断事务是否回滚,所有的数据库操作都需要用到一个connection,这个时候可以把connection和当前线程关联起来。


强软弱虚引用


5. 强引用:最常见的引用

Object o = new Object()

当没有印用指向new Object的时候,new Object()会被垃圾回收器回收

6. 软引用

package com.mupack;

import java.lang.ref.SoftReference;

import jdk.management.resource.internal.inst.ThreadRMHooks;

public class App
{ 
   
    public static void main(String[] args) throws Exception { 
   
        SoftReference<byte[]> softReference = new SoftReference<byte[]>(new byte[1024 * 1024 * 10]);
        //有值
        System.out.println(softReference.get());
        System.gc();
        //等一下垃圾回收,这个时候jvm内存充足,不会回收软引用。
        try { 
   
            Thread.sleep(500);
        } catch (Exception e) { 
   
            //TODO: handle exception
            e.printStackTrace();
        }
        //有值
        System.out.println(softReference.get());
        //这时候再定义一个数组,heap将装不下,这时候系统会垃圾回收,先回收一次,如果还不够,则把软引用也回收了。
        byte[] b = new byte[1024 * 1024 * 15];
        System.out.println(softReference.get());
    }
}

软引用实例:
当我们手动把jvm虚拟机内存调成20M,一开始定义一个10M数组,Heap够用,当时当第二次定义一个15M数组的时候,Heap不够用,所以垃圾回收器回收软引用。

3. 弱引用

package com.mupack;

import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;

import jdk.management.resource.internal.inst.ThreadRMHooks;

public class App
{ 
   
    public static void main(String[] args) throws Exception { 
   
        WeakReference<M> m = new WeakReference<>(new M());
        //有值
        System.out.println(m.get());
        System.gc();
        //没值
        System.out.println(m.get());

    }
}

弱引用遭遇到垃圾回收器(gc)就会被回收。

4. 虚引用

当一个虚引用被回收的时候,他会把信息传递到队列中去。虚引用其实并没有引用对象,只是当被回收的时候传递一个消息而已。

package com.mupack;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Queue;

import jdk.management.resource.internal.inst.ThreadRMHooks;

public class App { 
   
    static private Queue QUEUE;
    static private List LIST;

    public static void main(String[] args) throws Exception { 
   
        PhantomReference<M> phantomReference = new PhantomReference<M>(new M(), (ReferenceQueue<? super M>) QUEUE);
        new Thread(() -> { 
   
            while(true){ 
   
                LIST.add(new byte[1024 * 1024]);
                try { 
   
                    Thread.sleep(1000);
                } catch (Exception e) { 
   
                    //TODO: handle exception
                    e.printStackTrace();
                    Thread.currentThread().interrupt();
                }
                //一直打印null
                System.out.println(phantomReference.get());
            }
        }).start();
        

    }
}

在这里插入图片描述
DirectByteBuffer是JVM内存中的一片内存,用来指向对外内存(操作系统管理的内存),操作系统管理的内存JVM垃圾处理器不能回收,所以当DirectByteBuffer回收后,会在队列中有相应的记录,之后JVM会去处理对外内存。(JVM是C++写的所以能够处理操作系统内存)。


各种引用用途

  1. 强引用:最常用的一种引用
  2. 软引用:适合做缓存,比如内存中一张图片,当内存不够用的时候先把图片移动出去,当内存充足的时候再把图片load进来
  3. 弱引用:为了解决某些地方的内存泄露问题。
  4. 虚引用:主要是应用在与系统内存区域交互的时候,比如从网络中下载的数据,如果没有虚引用,我们需要先把数据加载到JVM内存才能让JVM管理,现在有了虚引用,直接用JVM中的DirectByteBuffer内存区域指向系统内存,当DirectByteBuffer回收后,会通知队列,这时候JVM垃圾回收器就知道去系统内存请理相应的系统内存空间

ThreadLocal底层

每个ThreadLocal对应一个ThreadLocalMap(ThreadLocal内部类),每一个ThreadLocal中存储多个Entry,每个Entry的key为ThreadLocal对象,value为响应的值

public Class Thread{ 
   
		...
	    ThreadLocal.ThreadLocalMap threadLocals = null;
	    ...
}

1. get()方法

public T get() { 
   
		//获取当前线程
        Thread t = Thread.currentThread();
        //获取Thread中的Map
        ThreadLocalMap map = getMap(t);
        if (map != null) { 
   
        	//获取Entry
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) { 
   
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        //当Thread中没有ThreadLocalMap或者ThreadLocalMap中找不到响应的key时执行初始化
        return setInitialValue();
    }

2. initialValue()方法

private T setInitialValue() { 
   
		//获取一个初始值,默认为null
        T value = initialValue();
        //获取当前线程
        Thread t = Thread.currentThread();
        //获取当前线程的Map
        ThreadLocalMap map = getMap(t);
        if (map != null)
        	//如果Map存在,则设置
            map.set(this, value);
        else
        	//如果Map不存在,则创建一个Map,并且赋值
            createMap(t, value);
        return value;
    }

3. set()方法

    public void set(T value) { 
   
    	//获取当前线程
        Thread t = Thread.currentThread();
        //获取当前线程的Map
        ThreadLocalMap map = getMap(t);
        //同上
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

4. remove()方法

public void remove() { 
   
		//获取当前线程的Map
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
         	//移除Map中的key,value
             m.remove(this);
     }

ThreadLocal中的内存泄露问题

Entry对象实际是一个弱引用,如果一个对象只有弱引用的时候,那当垃圾回收器遇见他的时候它就会被回收。

//Entry继承弱引用类
static class Entry extends WeakReference<ThreadLocal<?>> { 
   
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) { 
   
            	//调用父接口,令弱引用指向ThreadLocal变量
                super(k);
                value = v;
            }
        }

在这里插入图片描述
若是强引用,当tl = null之后,Thread线程对象还是会一直存在,这时会形成一个引用链(Thread->ThreadLocalMap->Entry->ThreadLocal),导致ThreadLocal仍然释放不了,所以ThreadLocal应该使用弱引用,当tl = null后垃圾回收器一遇见ThreadLocal就直接回收掉ThreadLocal。但是这样仍然会存在内存泄漏问题,即ThreadLocal被回收之后,key不存在,导致value无法被访问到,所以正确的用法应该是不用ThreadLocal的时候手动remove掉。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/169027.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • java开发培训_Java培训课程那里好?

    java开发培训_Java培训课程那里好?互联网时代,人们纷纷羡慕IT软件行业的前景和“钱景”。有些行动力较强的更是摩拳擦掌,直接通过参加Java培训班来成功转行这一行业。这些通过Java培训班转行成功的人现在如何了,他们的薪资高吗?没有基础但是想要跟上IT软件行业的发展步伐,选择Java培训班显然是一条有效途径。早几年,IT行业发展刚起步的时候,每一天都有无数家互联网企业诞生,对IT技术人才的需求规模巨大,学员从Java培训班毕业之后迅…

    2022年7月8日
    18
  • qtabwidget 样式_标注样式怎么设置合理

    qtabwidget 样式_标注样式怎么设置合理个人使用qt,感觉QTabwidget是个非常好用的控件,但有时候总是感觉其tab样式不好控制或说不够灵活,从而导致放弃使用该控件。比如说,标签横向显示的时候,文字随之也横着显示了,这样还需要指定自定义样式,继承QProxyStyle类并重写drawControl虚函数。然而这样过于麻烦,关于软件主菜单不同的界面切换,个人还是比较喜欢按键组合+STackedWidget控件。对于一遍的小界面来说,QTabWidget其实完全满足你的使用要求,所以本文主要简述QTabwidget样式的常用使用方法,配合标

    2022年9月2日
    5
  • 一个普通java程序员的10年…泪奔 o(╥﹏╥)o o(╥﹏╥)o[通俗易懂]

    一个普通java程序员的10年…泪奔 o(╥﹏╥)o o(╥﹏╥)o[通俗易懂]大家好,我是曹尼玛,是一个很普通java程序员,今天看了下日期,转眼间,毕业9年,工作10年了,经历比较多,受伤比较多,收获比较多,改变也很多,对未来也有一些规划,对java新人有一些中肯的建议…过去10年的java人生回顾人生回顾,酸甜苦辣,蛋定就好…我的原生家庭和学业本D89年出生在沿海地区的一个普通农村家庭,一直供我上学,感恩父母!上的是农村小学,乡镇初中,乡镇高中,学习成绩中上,加上教育一般,以及我智商一般,即使努力,也就考了个破本科,选了个不用拼爹的专业-计算机科学与技术。.

    2022年6月10日
    81
  • stm32编程步骤_单片机STM32

    stm32编程步骤_单片机STM32近几年来,从云计算、大数据到机器学习、AI、物联网,各种新潮的技术概念一波~~接一波。于是就产生了一些好奇心旺盛,抱着去凑一凑热闹的心态,实际上却是也想分一杯羹儿的程序员!但问题是,在他们的技术栈里,一切…

    2022年9月6日
    5
  • idea进入方法快捷键详情大全(idea快捷键大全最新设置)[通俗易懂]

    idea进入方法快捷键详情大全(idea快捷键大全最新设置)[通俗易懂]首页>软件应用>返回首页idea进入方法快捷键详情大全(idea快捷键大全最新设置)软件应用发布时间:2022-02-1311:05:09刚开始使用IDEA时一直都不熟悉,利用空闲的时间整理了一下常用的IDEA快捷键。1、Ctrl快捷键介绍Ctrl+F在当前文件进行文本查找Ctrl+R在当前文件进行文本替换Ctrl+Z撤销Ctrl+Y删除光标所在行或删除选中的行Ctrl+X剪切光标所在行或剪切选择内容…

    2022年10月2日
    1
  • java接口和抽象类的异同_抽象类的控制符是什么

    java接口和抽象类的异同_抽象类的控制符是什么之前Java接口中的方法默认都是public abstract,成员变量默认都是public static final,偶然发现接口中可以有default类型的方法,才知道java8中接口可以有自己的实现了。那么jdk1.8究竟对接口做了哪些修改呢?(1) 增加default方法。default方法作用范围也是public,只是有了具体实现的方法体。对已有的接口,如果想对接口增加一个新方法,那么需要对所有实现该接口的类进行修改。而有了default方法,可以解决该问题。(2) 新增static方法。st

    2022年8月8日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号