ThreadLocal
ThreadLocal은 클래스로, 이 스레드 로컬을 통해 각 스레드 별로 필요한 정보를 저장할 수 있다. 각 스레드는 자신만의 스레드 로컬을 가질 수 있으며, 다른 스레드의 스레드 로컬에는 접근할 수 없다. 각 스레드 별로 별도의 스레드 로컬을 갖기 때문에 멀티스레드 환경에서 공유 자원에 대한 동기화 문제를 피하고, 각 스레드가 자신의 상태를 안전하게 유지할 수 있다.
ThreadLocalMap
ThreadLocalMap은 ThreadLocal의 정적 내부 클래스이다. 모두 private으로 구성되어 있어서 외부에서 접근 가능한 메서드가 없다.
Thread 클래스는 ThreadLocalMap을 멤버로 갖는다. 즉, 각 스레드마다 ThreadLocalMap을 갖는다.
public class Thread implements Runnable {
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
// ...
}
ThreadLocal의 set과 get 메서드를 보면, 현재 스레드의 ThreadLocalMap 인스턴스를 가져와서 해당 자료구조에 값을 저장하고 조회하는 것을 알 수 있다. 그리고 set 메서드를 살펴보면 key-value에서 ThreadLocal가 key인 것을 알 수 있다.
public class ThreadLocal<T> {
// ...
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();
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
createMap(t, value);
}
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
}
그리고 ThreadLocalMap의 내부를 좀 더 살펴보면, Entry 배열을 통해(Entry[]) 특정 스레드의 ThreadLocal들을 관리한다. 그리고 key 매개변수로 넘어온 ThreadLocal 객체의 주소값을 통해 배열에 value를 가진 Entry를 저장한다.
private void set(ThreadLocal<?> key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
if (e.refersTo(key)) {
e.value = value;
return;
}
if (e.refersTo(null)) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
정리하면 다음과 같다.
- ThreadLocal 인스턴스는 내부적으로 현재 스레드의 ThreadLocalMap 인스턴스를 가져온다.
- ThreadLocalMap은 배열로 관리되는데 이 곳에 ThreadLocal 값을 저장하고, 이곳에서 ThreadLoal 값을 가져온다.
- 즉, 각 스레드마다 ThreadLocalMap을 갖는다. 그리고 각 ThreadLocal마다 자신의 스레드의 ThreadLocalMap 중 특정 위치(ThreadLocal 객체의 해시 코드를 키 값으로 접근하여)에 접근할 수 있게 되고, 그 곳에 자신의 값이 저장되어 있다.
Reference
'Language > Java' 카테고리의 다른 글
자바에서 자식 메서드가 부모 메서드가 던지는 체크 예외의 하위 타입만 던질 수 있는 이유 (0) | 2024.10.14 |
---|---|
스레드의 생명주기 (0) | 2024.10.14 |
Comparable과 Comparator (1) | 2024.01.15 |
추상 클래스와 인터페이스 비교하기 (0) | 2024.01.14 |
좋은 객체 지향 설계의 5가지 원칙(SOLID) (0) | 2023.12.28 |