ThreadLocal 원리 및 사용
6592 단어 java&javaweb
사용 방식 및 Set 부분 이해,생 성 포함
ThreadLocal threadLocal = new ThreadLocal<>();
threadLocal.set(1);
1.thread Local 이라는 저장 방식 은 내부 에 하나의 인용 만 저장 할 수 있 습 니 다.(인용 은 용 기 를 가리 킬 수 있 습 니 다.예 를 들 어 map 또는 integer 와 같은 단일)2.thread Local 의 내부 실현 은 현재 스 레 드 를 얻 은 다음 에 현재 스 레 드 에 저 장 된 스 레 드 map ThreadLocalMap 을 얻 는 것 입 니 다.
ThreadLocalMap 내부 구현
1.ThreadLocalMap 의 key 는 현재 실례 화 된 thread Local 의 주소 입 니 다.값 은 저 장 된 object 유형 2 입 니 다.map 내부 의 실현 은 16 으로 초기 화 되 고 확장 한도 값 은 16*0.75+1 의 enity 배열 3 입 니 다.enity 내 부 는 reference(참조)+object 의 형식 reference 는 key 이 고 object 는 값 입 니 다.
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 값 을 설정 할 때 현재 스 레 드 와 스 레 드 내부 에 저 장 된 ThreadLocalMap 형식의 map 를 가 져 오고 map 에 값 을 설정 합 니 다.현재 스 레 드 가 다른 thread Local 에 의 해 설정 되 지 않 았 다 면 map 는 초기 화 되 지 않 았 습 니 다.먼저 ThreadLocalMap 초기 화(hashMap 유사)를 진행 합 니 다.
private void set(ThreadLocal> key, Object value) {
...
for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
ThreadLocal> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
set 에 서 는 먼저 key.hashcode&15 의 위 치 를 계산 하여 현재 스 레 드 의 인용 을 배치 할 적당 한 위 치 를 꺼 냅 니 다.이 위치 에서 아래로 옮 겨 다 니 기 시작 합 니 다.값 이 비어 있 지 않 으 면 들 어가 서 검증 합 니 다.현재 enity 의 key==나의 key 라면 교체 값 이 현재 enity 가 비어 있 지 않 지만 enity 안의 인용 은 null 입 니 다.자원 이 방출 되 었 음 을 설명 합 니 다.replace StaleEntry 를 통 해 값 설정 작업 을 진행 합 니 다.마지막 으로 빈 노드 를 찾 으 면 새로운 entity 를 만 들 고 이 k+v 를 설치 하고 rehash 작업(확장)이 필요 한 지 판단 합 니 다.
private void replaceStaleEntry(ThreadLocal> key, Object value,
int staleSlot) {
...
//slotToExpunge = staleSlot , k null index
...
for (int i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) {
ThreadLocal> k = e.get();
// staleSlot , k==key, , key (slotToExpunge)
if (k == key) {
e.value = value;
tab[i] = tab[staleSlot];
tab[staleSlot] = e;
// , i ,
if (slotToExpunge == staleSlot)
slotToExpunge = i;
cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
return;
}
// If we didn't find stale entry on backward scan, the
// first stale entry seen while scanning for key is the
// first still present in the run.
// , slotToExpunge == staleSlot , slotToExpunge , ,k null, slotToExpunge map.length
if (k == null && slotToExpunge == staleSlot)
slotToExpunge = i;
}
// , , , ," "
tab[staleSlot].value = null;
tab[staleSlot] = new Entry(key, value);
// If there are any other stale entries in run, expunge them
// slotToExpunge!=staleSlot, staleSlot ( ), staleSlot ( slotToExpunge = i ), , , , cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
if (slotToExpunge != staleSlot)
cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
}
private int expungeStaleEntry(int staleSlot) {
//
tab[staleSlot].value = null;
tab[staleSlot] = null;
size--;
// ,
Entry e; int i;
for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) {
ThreadLocal> k = e.get();
if (k == null) {
...
//
} else {
int h = k.threadLocalHashCode & (len - 1);
// k hash i, , i
// e , hash ,( , , , , , (null )
if (h != i) {
tab[i] = null;
// Unlike Knuth 6.4 Algorithm R, we must scan until
// null because multiple entries could have been stale.
while (tab[h] != null)
h = nextIndex(h, len);
tab[h] = e;
}
}
}
// null
return i;
}
private boolean cleanSomeSlots(int i, int n) {
boolean removed = false;
Entry[] tab = table;
int len = tab.length;
do {
i = nextIndex(i, len);
Entry e = tab[i];
if (e != null && e.get() == null) {
n = len;
removed = true;
i = expungeStaleEntry(i);
}
} while ( (n >>>= 1) != 0);
return removed;
}
cleansome Slots 는 방금 얻 은 맨 앞 에 있 는 홈 점 부터 아래로 옮 겨 다 니 며 폐 기 된 인용 만 있 으 면 제거 하고 그 과정 에서 다시 교정 합 니 다.i=expunge StaleEntry 를 몇 번 더 교정 하여 폐 기 된 인용 이 없 는 존 재 를 확보 한 다음 에 상부 로 돌아 갑 니 다.reove 데이터 가 있 는 지 여 부 를 확인 합 니 다.
또 다른 문 제 는 메모리 유출 이 왜 매번 제때에 clean Some Slots 를 사용 해 야 하 는 지 입 니 다.여기 서 스 레 드 는 대상 을 인용 하고 대상 이 속 한 threadlocal 은 이미 풀 려 났 기 때 문 입 니 다.그러면 이 대상 은 존재 하지 않 을 것 입 니 다.그러나 제때에 풀 려 나 지 않 았 습 니 다.
get 논리
get 논리 가 간단 합 니 다.코드 를 붙 이지 않 습 니 다.주로 get 일 때 map 가 존재 하면 map 에서 찾 고 찾 으 면 되 돌려 줍 니 다.찾 는 과정 에서 폐 기 된 홈 점 이 발견 되면 청 소 를 하고 찾 지 못 하면 null 로 돌아 갑 니 다.map 가 존재 하지 않 으 면 기본 값 null 을 map 에 설정 하고 기본 값 initialValue 로 돌아 가 는 방법 은 proctected 입 니 다.복사 할 수 있 습 니 다.