알 기 쉬 운 학습 자바 ThreadLocal

6329 단어 JavaThreadLocal
머리말
ThreadLocal 은 변수 로 모든 스 레 드 에 복사 본 을 만 들 었 기 때문에 모든 스 레 드 는 자신의 내부 의 복사 본 변 수 를 방문 할 수 있 고 서로 다른 스 레 드 간 에 서로 간섭 하지 않 습 니 다.본 고 는 실제 장면 을 바탕 으로 ThreadLocal 이 어떻게 사용 하고 내부 실현 체 제 를 소개 할 것 이다.
응용 장면
Parameter 대상 의 데 이 터 는 여러 모듈 에서 사용 해 야 하 며,매개 변수 전달 방식 을 사용 하면 모듈 간 의 결합 성 을 증가 시 킬 수 있 습 니 다.우선 ThreadLocal 로 모듈 간 공유 데 이 터 를 어떻게 실현 하 는 지 살 펴 보 자.

class Parameter {
 private static ThreadLocal<Parameter> _parameter= new ThreadLocal<>();
 public static Parameter init() {
 _parameter.set(new Parameter());
 }
 public static Parameter get() {
 _parameter.get();
 }
 ...      
}
모듈 A 에서 Parameter.init 를 통 해 초기 화 합 니 다
  • 모듈 B 또는 모듈 C 에서 Parameter.get 방법 을 통 해 같은 스 레 드 에서 모듈 A 가 초기 화 된 Parameter 대상 을 얻 을 수 있 습 니 다
  • 실현 원리
    스 레 드 Thread 의 측면 에서 볼 때 모든 스 레 드 내부 에 ThreadLocalMap 인 스 턴 스 에 대한 인용 이 있 습 니 다.ThreadLocalMap 인 스 턴 스 는 스 레 드 의 부분 변수 공간 에 해당 하고 스 레 드 각자 의 데 이 터 를 저장 합 니 다.구체 적 으로 다음 과 같 습 니 다.
     
    Entry
    Entry 는 Weak Reference 류 에서 계승 되 며 스 레 드 의 개인 변 수 를 저장 하 는 데이터 구조 입 니 다.ThreadLocal 인 스 턴 스 를 인용 하면 ThreadLocal 인 스 턴 스 가 null 이면 table 에서 해당 하 는 Entry 를 삭제 할 수 있 음 을 의미 합 니 다.
    
    class Entry extends WeakReference<ThreadLocal<?>> {
     Object value;
     Entry(ThreadLocal<?> k, Object v) {
     super(k);
     value = v;
     }
    }
    ThreadLocalMap
    내부 에 table 배열 을 사용 하여 Entry 를 저장 합 니 다.기본 크기 INITIALCAPACITY(16),먼저 몇 가지 인 자 를 소개 합 니 다.
    size:table 에서 요소 의 수량
  • threshold:table 크기 의 2/3,size>=threshold 시 table 을 옮 겨 다 니 며 key 가 null 인 요 소 를 삭제 합 니 다.삭제 후 size>=threshold*3/4 시 table 을 확장 해 야 합 니 다
  • 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);
    }
    ThreadLocalMap getMap(Thread t) {
     return t.threadLocals;
    }
    위의 코드 에서 알 수 있 듯 이:
    현재 스 레 드 Thread 에서 ThreadLocalMap 인 스 턴 스 를 가 져 옵 니 다
  • ThreadLocal 인 스 턴 스 와 value 는 Entry 로 봉 인 됩 니 다
  • 이어서 Entry 가 table 배열 에 저장 하 는 방법 을 보 세 요.
    
    private void set(ThreadLocal<?> key, Object value) {
     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)]) {
     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();
    }
    1.ThreadLocal 의 nextHashCode 방법 으로 hash 값 을 생 성 합 니 다.
    
    private static AtomicInteger nextHashCode = new AtomicInteger();
    private static int nextHashCode() { 
     return nextHashCode.getAndAdd(HASH_INCREMENT);
    }
    nextHashCode 방법 을 통 해 알 수 있 듯 이 ThreadLocal 은 한 번 씩 예화 할 때마다 해시 값 은 원자 에 HASH 를 증가 합 니 다.INCREMENT。
    2.hash&(len-1)를 통 해 table 의 위치 i 를 찾 고 table 의 i 위치 요 소 를 f 로 가정 합 니 다.
    3.만약 f!=null,f 의 인용 을 k 로 가정 합 니 다.
  • k 가 현재 ThreadLocal 인 스 턴 스 와 일치 하면 value 값 을 수정 하고 되 돌려 줍 니 다
  • 4.567917.k 가 null 이 라면 이 f 는 이미 stale(오래된)요소 임 을 나타 낸다.replace StaleEntry 방법 을 사용 하여 table 의 모든 오래된 요소(즉,entry 의 인용 은 null)를 삭제 하고 새 요 소 를 삽입 하여 되 돌려 줍 니 다4.567917.그렇지 않 으 면 nextIndex 방법 으로 다음 요소 f 를 찾 아 3 단 계 를 계속 진행 합 니 다4.f===null 이면 Entry 를 table 의 i 위치 에 추가 합 니 다.
    5.cleanSomeSlots 를 통 해 오래된 요 소 를 삭제 합 니 다.table 에서 요소 가 삭제 되 지 않 으 면 현재 상황 에서 확장 할 지 여 부 를 판단 해 야 합 니 다.
    테이블 확장
    만약 에 table 의 요소 수량 이 한도 값 threshold 의 3/4 에 이 르 면 확장 작업 을 할 것 입 니 다.과정 은 매우 간단 합 니 다.
    
    private void resize() {
     Entry[] oldTab = table;
     int oldLen = oldTab.length;
     int newLen = oldLen * 2;
     Entry[] newTab = new Entry[newLen];
     int count = 0;
     for (int j = 0; j < oldLen; ++j) {
     Entry e = oldTab[j];
     if (e != null) {
     ThreadLocal<?> k = e.get();
     if (k == null) {
     e.value = null; // Help the GC
     } else {
     int h = k.threadLocalHashCode & (newLen - 1);
     while (newTab[h] != null)
     h = nextIndex(h, newLen);
     newTab[h] = e;
     count++;
     }
     }
     }
     setThreshold(newLen);
     size = count;
     table = newTab;
    }
    새 배열 new Tab 을 새로 만 듭 니 다.크기 는 원래 의 2 배 입 니 다
  • table 의 요 소 를 new Tab 로 복사 하고 오래된 요 소 를 무시 합 니 다.table 의 요소 e 가 new Tab 의 i 위치 로 복사 해 야 한다 고 가정 합 니 다.i 위치 에 요소 가 존재 하면 다음 빈 위 치 를 찾 아 삽입 합 니 다
  • ThreadLocal.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();
    }
    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);
    }
    현재 스 레 드 의 threadLocals 를 가 져 옵 니 다.
  • threadLocals 가 null 이 아니라면 ThreadLocalMap.getEntry 방법 으로 대응 하 는 entry 를 찾 습 니 다.인용 이 현재 key 와 일치 하면 바로 돌아 갑 니 다.그렇지 않 으 면 table 의 나머지 요소 에서 계속 일치 합 니 다
  • threadLocals 가 null 이면 setInitialValue 방법 으로 초기 화하 고 되 돌려 줍 니 다
  • 
    private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
     Entry[] tab = table;
     int len = tab.length;
     while (e != null) {
     ThreadLocal<?> k = e.get();
     if (k == key)
     return e;
     if (k == null)
     expungeStaleEntry(i);
     else
     i = nextIndex(i, len);
     e = tab[i];
     }
     return null;
    }
    총결산
    본 논문 의 소 개 를 통 해 여러분 들 은 ThreadLocal 에 대해 더욱 직관 적 이 고 뚜렷 한 인식 을 가 질 수 있 기 를 바 랍 니 다.
    이상 은 본 고의 모든 내용 입 니 다.본 고의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 도움 이 되 기 를 바 랍 니 다.또한 저 희 를 많이 지지 해 주시 기 바 랍 니 다!

    좋은 웹페이지 즐겨찾기