当前位置: 首页 > news >正文

用dw怎么做网站首页百度竞价有点击无转化

用dw怎么做网站首页,百度竞价有点击无转化,网站网络推广方式方法,公司资料模板ThreadLocal无论是在项目开发还是面试中都会经常碰到,它的重要性可见一斑,本篇文章就从ThreadLocal的使用、实现原理、核心方法的源码、内存泄漏问题等展开介绍一下。 一、什么是ThreadLocal ThreadLocal是java.lang下面的一个类,在JDK 1.2版…

ThreadLocal无论是在项目开发还是面试中都会经常碰到,它的重要性可见一斑,本篇文章就从ThreadLocal的使用、实现原理、核心方法的源码、内存泄漏问题等展开介绍一下。

一、什么是ThreadLocal

ThreadLocal是java.lang下面的一个类,在JDK 1.2版本加入,作者是Josh Bloch(集合大神)和Doug Lea(并发大神)

它提供了一种线程局部变量的方式,线程局部变量是指每个线程都拥有自己独立的变量副本,互不干扰,通过ThreadLocal,可以方便地在多线程环境下共享数据,同时不需要考虑线程安全性,这也是解决并发问题的途径之一。

例如:在web开发中,可以使用ThreadLocal来保存用户的登录信息,以便每个线程都能够独立地获取和修改自己的登录信息,避免了线程之间的干扰。

二、ThreadLocal的使用

ThreadLocal有四个方法,分别为:

  • protected T initialValue():返回此线程局部变量的初始值。

  • pubulic T get(): 返回当前线程局部变量的当前线程副本的值。如果这是线程第一次调用该方法,则创建并初始化此副本。

  • public void set(T value):将此线程局部变量的当前线程的副本设置为指定的值。

  • public void remove():移除此线程局部变量的当前线程的值。

下面使用ThreadLocal来模拟用户登录信息的场景:

ThreadLocal工具类:

public class CurrentUserHolder {public static ThreadLocal<User> threadLocal=new ThreadLocal<>();
​public static void setUser(User user){threadLocal.set(user);}
​public static User getUser(){if (Objects.nonNull(threadLocal.get())) {return threadLocal.get();}throw new RuntimeException("当前用户信息为空!");}
​public static void clearUser(){threadLocal.remove();}
}

User实体类:

@Data
public class User {private String name;private Integer age;
}

测试:

public class Test {public static void main(String[] args) {//用户登录User user = new User();user.setName("小黑子");user.setAge(18);//将用户信息保存在ThreadLocal中CurrentUserHolder.setUser(user);//在其它方法中,可以通过ThreadLocal获取用户信息User localUser = CurrentUserHolder.getUser();System.out.println(localUser);//输出:User(name=小黑子, age=18)//用户操作完成后,可以remove掉CurrentUserHolder.clearUser();}
}

ps:由于ThreadLocal是基于线程的,所以在不同的线程中,通过ThreadLocal获取的用户信息是独立的,这在多线程环境下非常有用,可以避免线程之间的数据混乱和冲突。

三、ThreadLocal的实现原理

直接上图!下图中基本描述出了ThreadThreadLocalMapThreadLocal三者之间的关系。

解释一下:

  • ThreadLocal中用于保存线程的独有变量的数据结构是一个内部类:ThreadLocalMap,也是k-v结构,key就是当前ThreadLocal对象,value就是我们要保存的值。

  • Thread类中维护了两个ThreadLocalMap成员变量,threadLocals和inheritableThreadLocals,它们的默认值是null,类型为ThreadLocal.ThreadLocalMap,也就是ThreadLocal类的一个静态内部类ThreadLocalMap,感兴趣的可以去看一下源码。

四、核心源码

4.1 ThreadLocalMap内部类

在静态内部类ThreadLocalMap中,维护了一个数据结构类型为Entry的数组,源码如下:

static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}
}

从源码中我们可以看到,Entry继承了一个ThreadLocal类型的弱引用并将其作为key,value为Object类型(也就是我们需要保存的值)

我们再来看一下它的成员变量:

//数组的默认初始化容量
private static final int INITIAL_CAPACITY = 16;
//Entry数组,大小必须为2的幂
private Entry[] table;
//数组内部元素个数
private int size = 0;
//数组扩容阈值,默认为0,创建ThreadLocalMap对象后会被重新设置
private int threshold; 

是不是有点熟悉,这几个变量和HashMap中的变量很类似,功能也类似。

最后看一下它的构造方法:

/**
* Construct a new map initially containing (firstKey, firstValue).
* ThreadLocalMaps are constructed lazily, so we only create
* one when we have at least one entry to put in it.
*/
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {table = new Entry[INITIAL_CAPACITY];int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);table[i] = new Entry(firstKey, firstValue);size = 1;setThreshold(INITIAL_CAPACITY);
}

注释翻译过来大概就是,该构造方法是懒加载的,只有我们创建一个Entry对象并需要放入到Entry数组的时候才会去初始化数组。

4.2 set()方法

接下来我们就介绍一下ThreadLocal常用的一些方法吧,首先看一下set()方法:

public void set(T value) {//获取当前线程Thread t = Thread.currentThread();//获取当前线程的ThreadLocalMap对象ThreadLocalMap map = getMap(t);if (map != null)// 如果map存在,则将当前ThreadLocal对象作为key,value作为value放入map中map.set(this, value);else// 如果map不存在,则创建一个新的ThreadLocalMap对象,并新建一个Entry放入该ThreadLocalMap, 调用set方法的ThreadLocal和传入的value作为该Entry的key和valuecreateMap(t, value);
}

解释:

  • 获取当前线程,拿到当前Thread的ThreadLocalMap对象。

  • 如果map存在,则将当前ThreadLocal对象作为key,value作为value放入map中。

  • 如果map不存在,则创建一个新的ThreadLocalMap对象,并新建一个Entry放入该ThreadLocalMap, 调用set方法的ThreadLocal和传入的value作为该Entry的key和value。

4.3 get()方法

源码如下:

public T get() {//获取当前线程Thread t = Thread.currentThread();//获取当前线程的ThreadLocalMap对象ThreadLocalMap map = getMap(t);if (map != null) {//map存在,通过this(当前ThreadLocal)获取EntryThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")//Entry不为空,返回该Entry的value值T result = (T)e.value;return result;}}//map不存在,调用setInitialValue()方法设置初始值return setInitialValue();
}

解释:

  • 通过当前线程获取ThreadLocalMap:

    • 如果map存在,则通过当前ThreadLocal获取对应的Entry,若Entry不为空,返回该Entry的value值。

    • 如果map不存在,则调用setInitialValue()方法设置初始值。

  • setInitialValue():

    • 根据initalValue()方法获取value值,默认值为null,可以重写该方法。

    • 通过当前线程获取ThreadLocalMap对象。

    • map存在,设置当前值为上述value,不存在则创建新的ThreadLocalMap,并将值设置为value。

4.4 remove()方法

源码如下:

public void remove() {//根据当前线程获取ThreadLocalMap对象ThreadLocalMap m = getMap(Thread.currentThread());if (m != null)//存在,执行remove方法m.remove(this);
}

解释:

  • 根据当前线程获取ThreadLocalMap对象,存在则执行remove()方法。remove(this)方法中,将ThreadLocal作为key来删除对应的Entry。

五、内存泄漏问题

5.1 分析

读到这,相信你对ThreadLocal的基本原理有了更深一步的理解,我们把上图补全,从堆栈视角看一下它们之间的引用关系。

我们可以看到,ThreadLocal对象,有两个引用,一个是栈上的ThreadLocal引用,一个是ThreadLocalMap中Key对它的引用。如果栈上的ThreadLocal引用不再使用了,那么ThreadLocal对象因为还有一条引用链在,所以会导致它无法回收,久而久之就会OOM。

这就是我们所说的ThreadLocal的内存泄漏问题,为了解决这个问题,ThreadLocalMap使用了弱引用,就是上述我们说过的Entry数组:

static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}
}

可以看出,ThreadLocal的引用k通过构造方法传递给了Entry类的父类WeakReference的构造方法,那么可以理解为ThreadLocalMap中的键是ThreadLocal的弱引用。

穿插一下Java中的四大引用:

  • 强引用:Java中默认的引用类型,只要引用还存在,即便OOM也不会被回收。

  • 软引用:内存不足时,将会被干掉。

  • 弱引用:无论内存充足与否,只要执行GC,就会被干掉。

  • 虚引用:最弱的一种引用,存在意义就是为了将关联虚引用的对象在被GC掉之后收到一个通知。

如果用了弱引用,那么ThreadLocal对象就可以在下次GC的时候被回收掉了。

这样做可以很大程度上避免了因为ThreadLocal的使用而导致的OOM问题,但也无法彻底避免

我们可以看到,虽然key是弱引用,但是value是强引用,而且它的生命周期是和Thread一样的,也就是说,只要Thread还在,那么这个对象就无法被回收。

那么,什么情况下,Thread会一直在呢,那就是线程池,这就导致value一直无法被回收。

5.2 如何解决

ThreadLocalMap底层使用数组来保存元素,使用“线性探测法”来解决hash冲突,在每次调用ThreadLocal的get、set、remove方法时,内部会实际调用ThreadLocalMap的get、set、remove等操作,而ThreaLocalMap的每次set、get、remove时,都会对key为null的Entry进行清除(expungeStateEntry()方法,将Entry的value清空,等下次GC就会被回收)。

所以,当我们一个ThreadLocal用完后,就手动remove一下,就可以在下次GC时,把Entry清理掉。

5.3 总结

上述我们分了两种情况来看ThreadLocal内存泄漏问题:

  • key使用强引用:引用ThreadLocal的对象被回收了,但是ThreadLocalMap持有ThreadLocal的强引用,如果没有手动remove,ThreadLocal不会被回收,导致Entry内存泄漏。

  • key使用弱引用:引用ThreadLocal被回收,由于ThreadLocalMap持有ThreadLocal的弱引用,即使没有手动remove,ThreadLocal也会被回收。value在下一次ThreadLocalMap调用set、get、remove的时候会被清除。

比较两种情况,我们可以发现:由于ThreadLocalMap的生命周期跟Thread一样长,如果都没有手动remove,就会导致内存泄漏,但是使用弱引用可以多一层保障:弱引用ThreadLocal被清理后key为null,对应的value在下一次ThreadLocalMap调用set、get、remove的时候可能会被清除。

因此,ThreadLocal内存泄漏的根源是:由于ThreadLocalMap的生命周期和Thread一样长,如果没有手动remove就会导致内存泄漏,而不是因为弱引用。

End:希望对大家有所帮助,如果有纰漏或者更好的想法,请您一定不要吝啬你的赐教🙋。

http://www.hkea.cn/news/241744/

相关文章:

  • 最好的做任务赚钱网站网络域名怎么查
  • 建设部规范网站百度app关键词优化
  • 骏域网站百度怎么收录网站
  • 网站robots.txt查看九江seo公司
  • 建设阿里妈妈网站搜索引擎排名优化seo
  • 自学网站建设作业创建网站免费
  • 营销网站定制的优势成品网站源码的优化技巧
  • 高职学院网站建设方案广告制作
  • table表格 做的网站营销案例分析报告模板
  • pc端网站做移动适配教育培训机构管理系统
  • 页游传奇排行榜无锡seo优化公司
  • 广西南宁网站设计百度seo算法
  • 网站建设服务怎么样近期国内热点新闻事件
  • 阿里巴巴网站国际站建设seo托管服务
  • 企业网站优化之如何做需求分析网奇seo赚钱培训
  • 施工企业会计制度收入确认规定百度自然排名优化
  • 校园网站建设意义网络营销的特点有哪些
  • 内江做网站哪里便宜google搜索关键词热度
  • 福建省建设银行招聘网站网络推广员压力大吗
  • 动态网站订单怎么做搜索引擎优化营销
  • html5行业网站最近有哪些新闻
  • 做网站业务的怎么寻找客户在哪里打广告效果最好
  • 广东深圳seo服务内容
  • 做网站怎么备案网络服务有限公司
  • 网站主页特效欣赏百度官网下载电脑版
  • php mysql开发网站开发任何小说都能搜到的软件
  • the7 wordpress主题宁波seo外包费用
  • 云南建筑培训网seo刷点击软件
  • 男女做暖网站h5页面制作平台
  • 可以做puzzle的网站百度关键词排名提升工具