>  기사  >  Java  >  스레드 지역 변수란 무엇입니까?

스레드 지역 변수란 무엇입니까?

零下一度
零下一度원래의
2017-07-21 14:34:262334검색

중국어로 번역된 ThreadLocal의 더 정확한 이름은 스레드 로컬 변수입니다.

 이건 무슨 용도인지, 저런 건 왜 있는 걸까요? 먼저 동시 프로그래밍 중에 멤버 변수는 처리가 수행되지 않으면 실제로 스레드에 안전하지 않다는 점을 설명하겠습니다. 각 스레드는 동일한 변수를 작동하며 이는 분명히 불가능하며 휘발성 키워드가 스레드 보안을 보장하지 않는다는 것도 알고 있습니다. 안전한. 따라서 어떤 경우에는 다음과 같은 조건을 충족해야 합니다. 즉, 변수는 동일하지만 각 스레드는 동일한 초기 값을 사용합니다. 즉, 동일한 변수의 새 복사본을 사용합니다. 이 경우 ThreadLocal은 매우 유용합니다. 예를 들어 DAO의 데이터베이스 연결은 DAO가 싱글톤이므로 해당 속성 Connection은 스레드로부터 안전한 변수가 아닙니다. 그리고 각 스레드는 이를 사용해야 하며, 각각은 자체 스레드를 사용합니다. 이 경우 ThreadLocal은 이 문제를 더 잘 해결합니다.

 이 문제를 소스코드 관점에서 분석합니다.

 먼저 ThreadLocal을 정의합니다.

   ThreadLocal<Connection> tl =  ThreadLocal<Connection>  Connection initConn = = DriverManager.getConnection("url, name and password"=( ==

package java.lang;import java.lang.ref.*;import java.util.concurrent.atomic.AtomicInteger;public class ThreadLocal<T> {   private final int threadLocalHashCode = nextHashCode();   private static AtomicInteger nextHashCode =new AtomicInteger();private static final int HASH_INCREMENT = 0x61c88647;private static int nextHashCode() {return nextHashCode.getAndAdd(HASH_INCREMENT);
    }protected T initialValue() {return null;
    }public ThreadLocal() {
    }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();
    }private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);if (map != null)
            map.set(this, value);elsecreateMap(t, value);return value;
    }public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);if (map != null)
            map.set(this, value);elsecreateMap(t, value);
    } public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());         if (m != null)
             m.remove(this);
     }ThreadLocalMap getMap(Thread t) {return t.threadLocals;
    }void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {return new ThreadLocalMap(parentMap);
    }T childValue(T parentValue) {throw new UnsupportedOperationException();
    }static class ThreadLocalMap {static class Entry extends WeakReference<ThreadLocal> {/** The value associated with this ThreadLocal. */Object value;

            Entry(ThreadLocal k, Object v) {super(k);
                value = v;
            }
        }private static final int INITIAL_CAPACITY = 16;private Entry[] table;private int size = 0;private int threshold; // Default to 0private void setThreshold(int len) {
            threshold = len * 2 / 3;
        }private static int nextIndex(int i, int len) {return ((i + 1 < len) ? i + 1 : 0);
        }private static int prevIndex(int i, int len) {return ((i - 1 >= 0) ? i - 1 : len - 1);
        }ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }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) {
                    ThreadLocal key = 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++;
                    }
                }
            }
        }private Entry getEntry(ThreadLocal key) {int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];if (e != null && e.get() == key)return e;elsereturn getEntryAfterMiss(key, i, e);
        }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);elsei = nextIndex(i, len);
                e = tab[i];
            }return null;
        }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();
        }private void remove(ThreadLocal key) {
            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.get() == key) {
                    e.clear();
                    expungeStaleEntry(i);return;
                }
            }
        }private void replaceStaleEntry(ThreadLocal key, Object value,                                       int staleSlot) {
            Entry[] tab = table;int len = tab.length;
            Entry e;int slotToExpunge = staleSlot;for (int i = prevIndex(staleSlot, len);
                 (e = tab[i]) != null;
                 i = prevIndex(i, len))if (e.get() == null)
                    slotToExpunge = i;for (int i = nextIndex(staleSlot, len);
                 (e = tab[i]) != null;
                 i = nextIndex(i, len)) {
                ThreadLocal k = e.get();if (k == key) {
                    e.value = value;

                    tab[i] = tab[staleSlot];
                    tab[staleSlot] = e;if (slotToExpunge == staleSlot)
                        slotToExpunge = i;
                    cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);return;
                }if (k == null && slotToExpunge == staleSlot)
                    slotToExpunge = i;
            }tab[staleSlot].value = null;
            tab[staleSlot] = new Entry(key, value);if (slotToExpunge != staleSlot)
                cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
        }private int expungeStaleEntry(int staleSlot) {
            Entry[] tab = table;int len = tab.length;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) {
                    e.value = null;
                    tab[i] = null;
                    size--;
                } else {int h = k.threadLocalHashCode & (len - 1);if (h != i) {
                        tab[i] = null;while (tab[h] != null)
                            h = nextIndex(h, len);
                        tab[h] = e;
                    }
                }
            }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;
        }private void rehash() {
            expungeStaleEntries();// Use lower threshold for doubling to avoid hysteresisif (size >= threshold - threshold / 4)
                resize();
        }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;
        }private void expungeStaleEntries() {
            Entry[] tab = table;int len = tab.length;for (int j = 0; j < len; j++) {
                Entry e = tab[j];if (e != null && e.get() == null)
                    expungeStaleEntry(j);
            }
        }
    }
}

이런 방식으로 동일한 연결이 사용되지만 각 연결은 새 연결이고 동일한 연결의 복사본입니다.

 그럼 구현 메커니즘은 무엇인가요?

1. 각 Thread 객체는 내부적으로 ThreadLocalMap을 유지합니다. 이러한 ThreadLocal 맵은 여러 ThreadLocal을 저장할 수 있습니다

/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */ThreadLocal.ThreadLocalMap threadLocals = null;

2. get() 메서드를 호출하면 먼저 현재 스레드를 가져온 다음 ThreadLocalMap으로 이동합니다. 현재 스레드의 객체가 비어 있지 않으면 ThreadLocal의 값을 꺼내고, 그렇지 않으면 초기화는 ThreadLocal의 값을 설정하는 것입니다.

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();
}

3. set() 메서드를 호출할 때 값을 ThreadLocal에 설정하는 것이 매우 일반적입니다.

4. 요약: get 메소드를 호출하면 실제로 각 현재 스레드에 ThreadLocal이 있습니다. 각 획득 또는 설정은 ThreadLocal에 대한 작업이며 다른 스레드와 별개입니다.

5. 응용 시나리오: ThreadLocal은 많은 스레드가 동일한 객체를 여러 번 사용해야 하고 객체가 동일한 초기화 값을 가져야 하는 경우에 사용하기에 가장 적합합니다.

6. 사실 아무리 말하더라도 더 명확하게 하려면 소스 코드를 보는 것이 더 좋습니다. 소스 코드를 보려면 WeakReference와 Map이 포함됩니다. 이 두 곳을 이해해야 합니다. 이 두 가지는 a.Java의 약한 참조입니다. 즉, 래핑된 참조는 도중에 파괴됩니다. GC.Object, 키인 이 threadLocal은 파괴될 수 있지만 우리가 정의한 클래스가 언로드되지 않는 한 tl의 강력한 참조는 항상 이 ThreadLocal을 참조하며 gc에 의해 제거되지 않습니다. b. HashMap과 유사합니다.

위 내용은 스레드 지역 변수란 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.