ThreadLocal源码分析

今天分析ThreadLocal

先看源码注释

* This class provides thread-local variables. These variables differ from
* their normal counterparts in that each thread that accesses one (via its
* get or set method) has its own, independently initialized
* copy of the variable. ThreadLocal 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变量。这些变量不同于普通副本,在每个线程中,它是一个独立的初始化拷贝。

Each thread holds an implicit reference to its copy of a thread-local

* variable as long as the thread is alive and the ThreadLocal
* 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 uniqueNum = new ThreadLocal() {
@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方法就只与当前线程有关,与其它线程的值无关

你可能感兴趣的:(ThreadLocal源码分析)