ThreadLocal 의 소스 코드 깊이 분석
private static final ThreadLocal<Integer> threadId = ThreadLocal.withInitial(() -> nextId.getAndIncrement());
private static final InheritableThreadLocal<Integer> threadId = new InheritableThreadLocal<Integer>() {
protected Integer initialValue() {
return nextId.getAndIncrement();
}
};
두 가지 방법 은 모두 방법 initial Value 를 다시 실 었 다.다만 첫 번 째 방법 은 더욱 교묘 할 뿐이다.사실은 본질 이 모두 같 고 상세 한 차 이 는 위의 글 을 볼 수 있다.
초기 화 대상 실행 스 레 드 에서 주로 통과 합 니 다.
MyThreadLocal.get();
대응 하 는 초기 화 값 을 가 져 옵 니 다.전체 소스 코드 의 분석 과정 은 이 get 방법 에서 시작 합 니 다.
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
우선 ThreadLocal 에서 유지 하 는 맵 을 가 져 옵 니 다.이 맵 에 이 값 이 있 으 면 이 값 을 되 돌려 줍 니 다.없 으 면 setInitialValue 를 통 해 값 을 설정 합 니 다.여기 서 주의해 야 할 것 은 이 ThreadLocalMap 아래 ThreadLocalMap 의 일부 코드 를 보십시오.
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expuniged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
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);
}
설명 으로 이해 하면 이 Entry 는 약 한 인용 형식 입 니 다.ThreadLocal 의 대상 이 비어 있 으 면 entry.get()도 비어 있 습 니 다.이 때 는 이 key 가 사용 되 지 않 는 다 는 것 을 의미 하기 때문에 이 entry 는 table 에서 지 울 수 있 습 니 다.이 entries 들 은 만 료 된 entries 라 고 불 릴 것 입 니 다.다음 코드 에 있 습 니 다.
ThreadLocalMap 의 전체 소스 코드 를 보면 쉽게 알 수 있 습 니 다.이 map 는 Entry 배열 로 데 이 터 를 저장 합 니 다.getEntry 방법 을 호출 할 때 먼저 key 의 hash 값 을 통 해 해당 Entry 배열 의 아래 표 시 를 찾 고 아래 표 시 를 통 해 해당 하 는 Entry 를 가 져 옵 니 다.e.get()을 호출 하면 대상 이 회수 되 었 다 고 생각 합 니 다.이때 회수 되 는 Reference 를 Entry 배열 에서 제거 하여 시스템 이 메모 리 를 회수 할 수 있 도록 해 야 합 니 다.
위의 getMap 방법 을 보면 이 스 레 드 가 가지 고 있 는 threadLocals 를 통 해 이 ThreadLocal 에 대응 하 는 value 가 있 는 지 찾 고 있 습 니 다.있 으 면 되 돌아 오고 없 으 면 초기 화 됩 니 다.초기 화 코드 는 다음 과 같 습 니 다.
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;
}
비교적 간단 해 보 입 니 다.다시 불 러 온 initialValue()를 호출 하여 대응 하 는 value 를 얻 은 다음 set 를 대응 하 는 map 대상 에 가 져 옵 니 다.위 는 ThreadLocal 의 전체 호출 과정 입 니 다.
다음은 Inheritable Thread Local 이 어떻게 부자 류 스 레 드 에서 대상 을 전파 하 는 지 에 대해 이야기 해 야 합 니 다.
먼저 Thread 의 원본 코드 를 보고 init 의 방법 을 보 세 요.(이 방법 은 스 레 드 가 초기 화 되 는 과정 에서 호출 됩 니 다)
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
}
/* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
if (g == null) {
g = parent.getThreadGroup();
}
}
/* checkAccess regardless of whether or not threadgroup is
explicitly passed in. */
g.checkAccess();
/*
* Do we have the required permissions?
*/
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
마지막 부분 을 주로 봤 어 요.
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
이 스 레 드 의 부모 스 레 드 의 inheritable ThreadLocals 가 비어 있 지 않 으 면 호출 합 니 다.
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals)
이것 이 바로 부모 스 레 드 의 값 을 하위 스 레 드 에 설정 하 는 방법 입 니 다.그러면 Inheritable Thread Local 이 inheritable Thread Locals 가 비어 있 지 않 고 createInherited Map 이라는 방법 으로 값 의 복 제 를 어떻게 실현 하 는 지 보 시 겠 습 니까?
Inheritable Thread Local 의 소스 코드 를 보 세 요.
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
/**
* Computes the child's initial value for this inheritable thread-local
* variable as a function of the parent's value at the time the child
* thread is created. This method is called from within the parent
* thread before the child is started.
*
* This method merely returns its input argument, and should be overridden
* if a different behavior is desired.
*
* @param parentValue the parent thread's value
* @return the child thread's initial value
*/
protected T childValue(T parentValue) {
return parentValue;
}
/**
* Get the map associated with a ThreadLocal.
*
* @param t the current thread
*/
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
/**
* Create the map associated with a ThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the table.
*/
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
ThreadLocal 의 일부 소스 코드 를 보고 있 습 니 다.
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
createInheritedMap 을 호출 하여 ThreadLocalMap 대상 으로 되 돌 아 왔 습 니 다.
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];
for (int j = 0; j < len; j++) {
Entry e = parentTable[j];
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}
그리고 이 방법 중 에...
Object value = key.childValue(e.value);
바로 Inheritable ThreadLocal 의 childValue 방법 을 호출 하여 들 어 오 는 값 을 직접 되 돌려 주 고 getMap 방법 에서 Inheritable ThreadLocal 을 사용 할 때 얻 은 map 는 이전의 thread Locals 에서 inheritable ThreadLocals 로 바 뀌 었 으 며 정 의 된 inheritable ThreadLocals 를 통 해 부자 류 스 레 드 대상 의 전달 을 실현 했다.
이상 은 모든 소스 코드 해석 부분 입 니 다.이해 하지 못 하 는 부분 이 있 으 면 아래 에 댓 글 을 남 겨 주세요~~~
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Is Eclipse IDE dying?In 2014 the Eclipse IDE is the leading development environment for Java with a market share of approximately 65%. but ac...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.