当前位置: 首页 > 后端技术 > PHP

ThreadLocal速览

时间:2023-03-30 00:06:02 PHP

欢迎点赞阅读,一起学习交流,有问题请留言。GitHub上还有一个开源的JavaHousewelcomestar1。ThreadLocal在Java8中引入,是一个泛型类。这个类可以提供线程变量。每个线程都有自己的变量。这意味着什么?每个线程都有自己的资源,就像现实生活中一样,每个程序员都有自己的对象,无需竞争,绝对线程安全。那么ThreadLocal是如何工作的呢?2类说明*该类提供线程局部变量。这些变量不同于*它们的正常对应物,因为每个访问一个变量的线程(通过它的*{@codeget}或{@codeset}方法)都有自己的、独立初始化的*变量副本。{@codeThreadLocal}实例通常是希望将状态与线程相关联的类中的私有*静态字段(例如,*用户ID或事务ID)。这是上面对ThreadLocal类的描述。大概意思就是提供线程变量,一般是静态字段修饰的。3创建ThreadLocal有两种创建方式,一种是通过原始的无参构造函数,另一种是使用Java8的lamaba表达式。3.1无参构造器源代码/***创建一个线程局部变量。*@see#withInitial(java.util.function.Supplier)*/publicThreadLocal(){}使用并初始化privatestaticfinalThreadLocalthreadId=newThreadLocal(){@OverrideprotectedIntegerinitialValue(){返回1;}};3.2lamaba表示式/***创建一个线程局部变量。变量的初始值是*通过调用{@codeSupplier}上的{@codeget}方法确定的。**@param线程本地值的类型*@paramsupplier用于确定初始值的供应商*@return一个新的线程局部变量*@throwsNullPointerException如果指定的供应商为空*@since1.8*/publicstaticThreadLocalwithInitial(Suppliersupplier){returnnewSuppliedThreadLocal<>(supplier);}使用并开始化privatestaticfinalThreadLocalthreadId=ThreadLocal.withInitial(()->1);其实如果你用IDEA,编译器也会提示你可以有lamaba,不过看看里面的源码还是蛮有意思的。4getter()方法/***返回此线程局部变量的当前线程副本中的值。如果变量对于当前线程没有值,它首先被初始化为返回的值*通过调用{@link#initialValue}方法。**@return这个线程局部的当前线程的值*/publicTget(){Threadt=Thread.currentThread();ThreadLocalMapmap=getMap(t);if(map!=null){ThreadLocalMap.Entrye=map.getEntry(this);if(e!=null){@SuppressWarnings("unchecked")T结果=(T)e.value;返回结果;}}returnsetInitialValue();}这里可以看到获取当前线程的Thread.currentThread和ThreadLocalMap类,它是一个hash结构(key-value)。getMap()方法通过当前线程获取它。然后以this关键字为key获取对应的value值。当然,如果为空,则返回初始化值。5什么情况下setter()方法不会返回初始化的默认值?答案是调用setter()方法。先看源码/***设置当前线程的这个线程局部变量的副本*为指定值。大多数子类将不需要*覆盖此方法,仅依靠{@link#initialValue}*方法来设置线程局部变量的值。**@paramvalue要存储在当前线程的副本中的值*这个线程局部的。*/publicvoidset(Tvalue){线程t=Thread.currentThread();ThreadLocalMapmap=getMap(t);if(map!=null)map.set(this,value);elsecreateMap(t,value);}大致意思是将当前线程作为key,将要设置的value作为hash结构中ThreadLocalMap中的value。看到这里就知道为什么ThreadLocal可以提供线程变量了。他说每个线程都是单独存储的,每个线程都有自己独立的资源,不存在资源共享,所以是线程安全的。6内存泄漏每个线程变量都放到一个ThreadLocalMap中,不会出现内存问题吗?我截取了部分源码staticclassThreadLocalMap{staticclassEntryextendsWeakReference>{Objectvalue;条目(ThreadLocal<?>k,对象v){超级(k);值=v;}}}可以看到这里提到了WeakReference类,可以知道ThreadLocalMap类是弱引用。按道理来说,线程一般执行完后,会被虚拟机的垃圾回收机制回收。但事实真的如此吗?如果是在线程池环境下,线程一直存在,那么ThreadLocal就会变成强引用,无法回收。所以存在内存泄漏问题。这就是remove()方法的用武之地。根据字面意思可以知道,这个方法可以清除线程变量资源,事实也是如此。因此,在程序结束时,最好调用remove()方法,以防止内存泄漏。参考《实战Java高并发程序设计》ThreadLocal源码关注微信公众号,随时手机阅读