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

并发编程:ThreadLocal

时间:2023-04-02 09:47:10 Java

大家好,我是小黑,一名生活在互联网上的农民工。从上一期并发编程:synchronized中我们了解到,要保证并发条件下共享资源的安全访问,就需要锁。但是加锁通常会降低运行效率,那么有没有办法在不影响效率的情况下完全避免对共享资源的竞争呢?答案就是小黑今天要告诉大家的ThreadLocal。什么是线程本地?此类提供线程局部变量。这些变量不同于它们的正常对应物,因为访问变量(通过其get或set方法)的每个线程都有自己的局部变量,独立于变量的初始化副本。ThreadLocal实例通常是类中的私有静态字段,它们希望将状态与特定线程(例如,用户ID或事务ID)相关联。以上源码官方API。可以概括为两点:ThreadLocal提供了get/set方法,可以访问属于当前线程的变量,也就是可以保证每个线程的变量是不同的。ThreadLocal在使用时通常定义为privatestatic。从字面意思来看,ThreadLocal可以认为是本地线程。其实ThreadLocal并不是一个线程,而是线程Thread的一个局部变量。如何使用首先,定义一个私有的静态ThreadLocal对象。privatestaticThreadLocalthreadLocal=newThreadLocal<>();然后每个线程都可以将当前线程的需求存储在一个局部变量中,并从中获取。publicvoidsetAndGet(Stringname){threadLocal.set(name);Strings=threadLocal.get();}最后,使用后需要将ThreadLocal中的值去掉。publicvoidremove(){threadLocal.remove();}原理那么ThreadLocal是如何保证每个线程获取到的数据是不同的呢?让我们看一下源代码。publicvoidset(Tvalue){Threadt=Thread.currentThread();//从当前线程获取一个ThreadLocalMapThreadLocalMapmap=getMap(t);if(map!=null)map.set(this,value);elsecreateMap(t,value);}voidcreateMap(Threadt,TfirstValue){t.threadLocals=newThreadLocalMap(this,firstValue);}我们发现在set方法中,会创建一个ThreadLocalMap,然后是value待设置的会放在这个Map中,当前的ThreadLocal对象作为key;然后将这个ThreadLocalMap赋值给Thread的threadLocals。如果查看Thread类的代码,会发现Thread类中有两个变量threadLocals和inheritableThreadLocals,它们的类型都是ThreadLocal.ThreadLocalMap。从下图可以看出Thread、ThreadLocal、ThreadLocalMap的关系。这个时候我们再来看一下get()和remove()方法的代码。publicTget(){线程t=Thread.currentThread();ThreadLocalMapmap=getMap(t);if(map!=null){ThreadLocalMap.Entrye=map.getEntry(this);if(e!=null){@SuppressWarnings("unchecked")T结果=(T)e.value;返回结果;}}returnsetInitialValue();}ThreadLocalMapgetMap(Threadt){returnt.threadLocals;}publicvoidremove(){ThreadLocalMapm=getMap(Thread.currentThread());}if(m!=null)m.remove(this);}可以看出,如上图的结构,get()方法是从Thread中取出ThreadLocalMap,然后以ThreadLocal对象为Key来取出值;remove()方法取出ThreadLocalMap,移除ThreadLocal对应的数据。那么ThreadLocalMap到底是什么?它是java.util.Map接口的子类吗?让我们看一下源代码。staticclassThreadLocalMap{staticclassEntryextendsWeakReference>{/**与此ThreadLocal关联的值。*/对象值;条目(ThreadLocal<?>k,对象v){超级(k);值=v;}}privatestaticfinalintINITIAL_CAPACITY=16;私人条目[]表;私有整数大小=0;私有整数阈值;//默认为0//省略其他代码}通过源码发现ThreadLocalMap并没有实现Map接口,也没有集成任何其他的Map类。它是在ThreadLocal类中定义的静态内部类。而且它的结构和HashMap的结构非常相似。有一个Entry[]数组来存储数据。而这个Entry类是继承自WeakReference类的子类,与HashMap不同。ThreadLocalMap和HashMap的结构有很多区别,但是还有一点不同于HashMap。我们看一下ThreadLocalMap的set方法。privatevoidset(ThreadLocalkey,Objectvalue){Entry[]tab=table;intlen=tab.length;//计算tab中放置的下标对应的keyinti=key.threadLocalHashCode&(len-1);//循环遍历tab,首先获取下标i对应的对象,如果不为空,则执行循环体//如果不是同一个threadLocal或者location无效,则寻找下一个location。for(Entrye=tab[i];e!=null;e=tab[i=nextIndex(i,len)]){ThreadLocalk=e.get();//判断当前ThreadLocal是否为同一个对象,如果是则替换value,endif(k==key){e.value=value;返回;}//如果k==null表示threadLocalkey无效,替换无效keyif(k==null){replaceStaleEntry(key,value,i);返回;}}tab[i]=newEntry(key,value);intsz=++大小;if(!cleanSomeSlots(i,sz)&&sz>=threshold)rehash();}循环遍历代码中的tab,首先获取下标i对应的对象,如果不为空,则执行循环体,如果不满足循环体中的两个条件,则执行nextIndex()方法。该方法的代码如下:privatestaticintnextIndex(inti,intlen){return((i+1

猜你喜欢