ThreadLocal源码分析

今天分析ThreadLocal

先看源码注释

* This class provides thread-local variables. These variables differ from
* their normal counterparts in that each thread that accesses one (via its
* <tt> get </tt> or <tt> set </tt> method) has its own, independently initialized
* copy of the variable. <tt> ThreadLocal </tt> instances are typically private
* static fields in classes that wish to associate state with a thread (e.g.,
* a user ID or Transaction ID).

大致意思是:这个类提供thread-local变量。这些变量不同于普通副本,在每个线程中,它是一个独立的初始化拷贝。

<p> Each thread holds an implicit reference to its copy of a thread-local
* variable as long as the thread is alive and the <tt> ThreadLocal </tt>
* instance is accessible; after a thread goes away, all of its copies of
* thread-local instances are subject to garbage collection (unless other
* references to these copies exist).

只要线程是存活的并且ThreadLocal对象可以访问,那么每个线程拥有一个隐式的引用,指向thread-local变量。当线程结束后,所有thread-local对象的拷贝都会受制于gc。

写一个使用ThreadLocal的范例


public class UniqueThreadIdGenerator {
private static AtomicInteger uniqueId = new AtomicInteger( 0 );

private static ThreadLocal<Integer> uniqueNum = new ThreadLocal<Integer>() {
@Override
// 如果当前线程是第一次请求id的分配则给它赋一个初始值
protected Integer initialValue() {
System. out .println( "initalValue" );
return uniqueId .getAndIncrement();
}
};

// 给当前线程返回它的id
public static int getCurrentThreadId() {
return uniqueNum .get();
}

// 设置当前线程的id
public static void setCurrentThreadId( int id) {
uniqueNum .set(id);
}

}

public class Thread1 implements Runnable{

@Override
public void run() {
// 线程的id是在它第一次run的时候才分配的,它run,它请求分配id,系统给它一个id
int id = UniqueThreadIdGenerator. getCurrentThreadId ();
System. out .println(Thread. currentThread ().getName() + " is running, its ID is: " + id);

// 三次向系统请求数据
for ( int i = 0 ; i < 3 ; i++) {
try {
Thread. sleep ( 1000 );
} catch (InterruptedException e) {
e.printStackTrace();
}
System. out .println(Thread. currentThread ().getName() + " is asking for data, my ID is:" + id);
}
System. out .println(Thread. currentThread ().getName() + " is over!----------" );
}


}


@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout. activity_main );


Thread ta = new Thread( new Thread1(), "A" );
Thread tb = new Thread( new Thread1(), "B" );
Thread tc = new Thread( new Thread1(), "C" );

ta.start();
tb.start();
tc.start();

}



运行结果:
07-04 05:43:44.873 12683-12701/com.xlz.dagger2_test I/System.out: initalValue
    C is running, its ID is: 0
07-04 05:43:44.874 12683-12699/com.xlz.dagger2_test I/System.out: initalValue
07-04 05:43:44.875 12683-12699/com.xlz.dagger2_test I/System.out: A is running, its ID is: 1
07-04 05:43:44.883 12683-12700/com.xlz.dagger2_test I/System.out: initalValue
    B is running, its ID is: 2
07-04 05:43:45.874 12683-12701/com.xlz.dagger2_test I/System.out: C is asking for data, my ID is:0
07-04 05:43:45.876 12683-12699/com.xlz.dagger2_test I/System.out: A is asking for data, my ID is:1
07-04 05:43:45.884 12683-12700/com.xlz.dagger2_test I/System.out: B is asking for data, my ID is:2
07-04 05:43:46.877 12683-12701/com.xlz.dagger2_test I/System.out: C is asking for data, my ID is:0
07-04 05:43:46.878 12683-12699/com.xlz.dagger2_test I/System.out: A is asking for data, my ID is:1
07-04 05:43:46.887 12683-12700/com.xlz.dagger2_test I/System.out: B is asking for data, my ID is:2
07-04 05:43:47.879 12683-12701/com.xlz.dagger2_test I/System.out: C is asking for data, my ID is:0
07-04 05:43:47.880 12683-12701/com.xlz.dagger2_test I/System.out: C is over!----------
07-04 05:43:47.882 12683-12699/com.xlz.dagger2_test I/System.out: A is asking for data, my ID is:1
07-04 05:43:47.883 12683-12699/com.xlz.dagger2_test I/System.out: A is over!----------
07-04 05:43:47.889 12683-12700/com.xlz.dagger2_test I/System.out: B is asking for data, my ID is:2
    B is over!----------



可以看到,线程第一次使用ThreadLocal时会调用InitialValue方法

在获取线程相关的id时,是调用的ThreadLocal的get方法,我们以这个方法为入口,分析源码

/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the { @link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread. currentThread ();
ThreadLocalMap map = getMap(t);
if (map != null ) {
ThreadLocalMap.Entry e = map.getEntry( this );
if (e != null )
return ( T )e. value ;
}
return setInitialValue();
}


get是返回当前线程的ThreadLocal变量的一个拷贝,如果这个变量没有值,则调用initalValue。

这里有个ThreadLocalMap,看下ThreadLocalMap是个啥

/**
* ThreadLocalMap is a customized hash map suitable only for
* maintaining thread local values. No operations are exported
* outside of the ThreadLocal class. The class is package private to
* allow declaration of fields in class Thread. To help deal with
* very large and long-lived usages, the hash table entries use
* WeakReferences for keys. However, since reference queues are not
* used, stale entries are guaranteed to be removed only when
* the table starts running out of space.
*/
static class ThreadLocalMap {


ThreadLocalMap是TheadLocal中的一个静态内部类,只适用于保存thread-local变量
每个Thead中都有一个ThreadLocalMap,看getMap(t)方法

ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}

就是获取Thread对象中的ThreadLocalMap成员

继续看get方法

if (map != null ) {
ThreadLocalMap.Entry e = map.getEntry( this );

如果map不为空,则从map中getEntry,看这个getEntry

private Entry getEntry(ThreadLocal key) {
int i = key.threadLocalHashCode & ( table . length - 1 );
Entry e = table [i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}

就是从这个table中获取value,这个table是一个数组,用来存放value的,这个table是ThreadLocalMap中的成员

key是ThreadLocal对象

那么这样一来,由于ThreadLocalMap是每个线程独有的,即便ThreadLocal的相同,即key相同,但是table不同,取到的value也是线程独有的


上面分析的是map不为空的情况,如果map为空的话,会调用

setInitialValue();

点进去

private T setInitialValue() {
T value = initialValue();
Thread t = Thread. currentThread ();
ThreadLocalMap map = getMap(t);
if (map != null )
map.set( this , value);
else
createMap(t, value);
return value;
}

这里先调用initValue()方法

protected T initialValue() {
return null ;
}

initialValue方法是protected方法,这里默认返回null
可以在子类中重写这个方法,给ThreadLocal变量给一个初值

如果map为null,则调用createMap()
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap( this , firstValue);
}

可以看到,就是给Thread里面new了一个ThreadLocalMap

get方法的分析就到这里,可以知道ThreadLocal的get方法其实就是从Thead中ThreadLocalMap中获取value,每个Thread的ThreadLocalMap是不同,所以ThreadLocal获取的value也是每个线程独立的值


下面分析ThreadLocal的set方法

public void set( T value) {
Thread t = Thread. currentThread ();
ThreadLocalMap map = getMap(t);
if (map != null )
map.set( this , value);
else
createMap(t, value);
}



这里可以看到,set方法是设置当前线程的TheradLocalMap里面的value,那么这个set方法就只与当前线程有关,与其它线程的值无关

以下是对提供的参考资料的总结,按照要求结构化多个要点分条输出: 4G/5G无线网络优化与网规案例分析: NSA站点下终端掉4G问题:部分用户反馈NSA终端频繁掉4G,主要因终端主动发起SCGfail导致。分析显示,在信号较好的环境下,终端可能因节能、过热保护等原因主动释放连接。解决方案建议终端侧进行分析处理,尝试关闭节电开关等。 RSSI算法识别天馈遮挡:通过计算RSSI平均值及差值识别天馈遮挡,差值大于3dB则认定有遮挡。不同设备分组规则不同,如64T和32T。此方法可有效帮助现场人员识别因环境变化引起的网络问题。 5G 160M组网小区CA不生效:某5G站点开启100M+60M CA功能后,测试发现UE无法正常使用CA功能。问题原因在于CA频点集标识配置错误,修正后测试正常。 5G网络优化与策略: CCE映射方式优化:针对诺基亚站点覆盖农村区域,通过优化CCE资源映射方式(交织、非交织),提升RRC连接建立成功率和无线接通率。非交织方式相比交织方式有显著提升。 5G AAU两扇区组网:与三扇区组网相比,AAU两扇区组网在RSRP、SINR、下载速率和上传速率上表现不同,需根据具体场景选择适合的组网方式。 5G语音解决方案:包括沿用4G语音解决方案、EPS Fallback方案和VoNR方案。不同方案适用于不同的5G组网策略,如NSA和SA,并影响语音连续性和网络覆盖。 4G网络优化与资源利用: 4G室分设备利旧:面对4G网络投资压减与资源需求矛盾,提出利旧多维度调优策略,包括资源整合、统筹调配既有资源,以满足新增需求和提质增效。 宏站RRU设备1托N射灯:针对5G深度覆盖需求,研究使用宏站AAU结合1托N射灯方案,快速便捷地开通5G站点,提升深度覆盖能力。 基站与流程管理: 爱立信LTE基站邻区添加流程:未提供具体内容,但通常涉及邻区规划、参数配置、测试验证等步骤,以确保基站间顺畅切换和覆盖连续性。 网络规划与策略: 新高铁跨海大桥覆盖方案试点:虽未提供详细内容,但可推测涉及高铁跨海大桥区域的4G/5G网络覆盖规划,需考虑信号穿透、移动性管理、网络容量等因素。 总结: 提供的参考资料涵盖了4G/5G无线网络优化、网规案例分析、网络优化策略、资源利用、基站管理等多个方面。 通过具体案例分析,展示了无线网络优化中的常见问题及解决方案,如NSA终端掉4G、RSSI识别天馈遮挡、CA不生效等。 强调了5G网络优化与策略的重要性,包括CCE映射方式优化、5G语音解决方案、AAU扇区组网选择等。 提出了4G网络优化与资源利用的策略,如室分设备利旧、宏站RRU设备1托N射灯等。 基站与流程管理方面,提到了爱立信LTE基站邻区添加流程,但未给出具体细节。 新高铁跨海大桥覆盖方案试点展示了特殊场景下的网络规划需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值