올바른 ThreadLocal 이해(niumd 시작)

9800 단어 ThreadLocal 스레드
우선ThreadLocal은 공유 대상의 다중 루틴 접근 문제를 해결하는 데 사용되는 것이 아니라 일반적인 상황에서ThreadLocal을 통해set ()에서 라인에 있는 대상은 이 라인이 스스로 사용하는 대상이고 다른 라인은 접근할 필요가 없고 접근할 수 없습니다.각 라인에서 방문하는 것은 서로 다른 대상이다.또한ThreadLocal은 각 라인이 각자의 독립된 대상을 유지할 수 있도록 한다.ThreadLocal을 통해 하는 것이 아니다.set () 는 모든 라인의 new 대상의 조작을 통해 만들어진 대상입니다. 모든 라인은 대상의 복사나 복사본이 아닌 대상을 만듭니다.ThreadLocal을 통해set () 은 이 새로 만든 대상의 인용을 각 라인의 자신의 맵에 저장합니다. 각 라인마다 이 맵이 있습니다. ThreadLocal을 실행합니다.get () 시 각 라인은 자신의 맵에서 넣은 대상을 꺼내기 때문에 각각의 라인에서 꺼낸 대상이고 ThreadLocal 실례는 맵의 키로 사용됩니다.ThreadLocal.set () 들어가는 것은 원래 여러 개의 라인이 공유하는 같은 대상이고, 그 많은 라인의 ThreadLocal이다.get () 가 얻은 것은 이 공유 대상 자체입니다. 병렬 접근 문제가 있습니다.다음은 hibernate의 일반적인 ThreadLocal 애플리케이션입니다.
private static final ThreadLocal threadSession = new ThreadLocal();  
  
public static Session getSession() throws InfrastructureException {  
    Session s = (Session) threadSession.get();  
    try {  
        if (s == null) {  
            s = getSessionFactory().openSession();  
            threadSession.set(s);  
        }  
    } catch (HibernateException ex) {  
        throw new InfrastructureException(ex);  
    }  
    return s;  
}  

getSession () 방법에서 현재 라인에session이 들어갔는지 안 들어갔는지 먼저 판단할 수 있습니다. 그렇지 않으면sessionFactory () 를 통해 알 수 있습니다.세션을 만들고 세션을 라인에 set합니다. 실제는 현재 라인의 ThreadLocalMap 맵에 저장됩니다. 이 세션에 대한 유일한 인용은 현재 라인의 ThreadLocalMap (아래 설명) 이고,threadSession은 이 값의 키입니다. 이session을 얻으려면threadSession을 통과할 수 있습니다.get () 을 사용하면 현재 라인에서 ThreadLocalMap을 얻은 다음threadSession을 키로 해서 해당하는 값을 꺼냅니다.이session은 퍼블릭이 아닌 라인의 개인 변수에 해당한다.분명히 다른 라인에서는 이session을 찾을 수 없고, 그들도 자신의ThreadLocalMap에 있는 것만 찾을 수 있다.세션이 여러 라인을 공유해서 사용한다면 혼란스럽지 않을 것이다.ThreadLocal 없이 어떻게 이루어질지 생각해 보세요.액션에서session을 만들고 서비스와dao에 하나씩 보내야 할 수도 있습니다. 귀찮아요.또는 정적 맵을 스스로 정의할 수 있다. 현재thread를 키로 하고 만든session을 값으로 하고put을 맵에 넣어도 된다. 이것도 일반인의 생각이다. 그러나 사실상ThreadLocal의 실현은 정반대이다. 이것은 모든 라인에 하나의 맵이 있고 ThreadLocal의 실례를 키로 하면 모든 맵의 항목 수가 적고 라인이 소각될 때 상응하는 물건도 함께 소각된다.이런 것 외에 또 어떤 좋은 점이 있는지 모르겠다.한 마디로 하면ThreadLocal은 대상 공유 접근 문제를 해결하는 데 쓰이는 것이 아니라 주로 대상을 유지하는 방법과 파라미터 전달을 피하는 편리한 대상 접근 방식을 제공한다.두 가지를 귀납했다.각 스레드에는 자신의 ThreadLocalMap 클래스의 대상이 있는데 스레드 자체의 대상을 그 안에 유지할 수 있고 각각의 스레드가 자신의 대상에 정확하게 접근할 수 있다. 2.공용ThreadLocal 정적 실례를 키로 하고, 서로 다른 대상의 인용을 서로 다른 라인의ThreadLocalMap에 저장한 다음, 라인이 실행되는 곳곳에서 정적 ThreadLocal 실례의 get () 방법을 통해 자신의 라인이 저장된 대상을 가져와 이 대상을 매개 변수로 전달하는 번거로움을 피합니다.물론 원래 라인을 공유하는 대상을 ThreadLocal을 통해set()를 라인에 넣으면 매개 변수의 전달을 피할 수 있지만 get()은 같은 공유 대상에 도달하고 방문 문제는 다른 수단으로 해결해야 한다는 것을 주의해야 한다.그러나 일반적으로 스레드 공유의 대상은 특정한 정적 변수로 설정하면 편리한 접근을 할 수 있기 때문에 스레드에 넣을 필요가 없을 것 같다.ThreadLocal의 응용 장소에서 가장 적합한 것은 라인에 따라 여러 개의 실례(각 라인이 하나의 실례에 대응하는) 대상에 대한 접근이고 이 대상은 많은 곳에서 사용해야 한다고 생각합니다.다음은 ThreadLocal의 실현 원리(jdk1.5 원본)를 살펴보겠습니다.
public class ThreadLocal<T> {  
    /** 
     * ThreadLocals rely on per-thread hash maps attached to each thread 
     * (Thread.threadLocals and inheritableThreadLocals).  The ThreadLocal 
     * objects act as keys, searched via threadLocalHashCode.  This is a 
     * custom hash code (useful only within ThreadLocalMaps) that eliminates 
     * collisions in the common case where consecutively constructed 
     * ThreadLocals are used by the same threads, while remaining well-behaved 
     * in less common cases. 
     */  
    private final int threadLocalHashCode = nextHashCode();  
  
    /** 
     * The next hash code to be given out. Accessed only by like-named method. 
     */  
    private static int nextHashCode = 0;  
  
    /** 
     * The difference between successively generated hash codes - turns 
     * implicit sequential thread-local IDs into near-optimally spread 
     * multiplicative hash values for power-of-two-sized tables. 
     */  
    private static final int HASH_INCREMENT = 0x61c88647;  
  
    /** 
     * Compute the next hash code. The static synchronization used here 
     * should not be a performance bottleneck. When ThreadLocals are 
     * generated in different threads at a fast enough rate to regularly 
     * contend on this lock, memory contention is by far a more serious 
     * problem than lock contention. 
     */  
    private static synchronized int nextHashCode() {  
        int h = nextHashCode;  
        nextHashCode = h + HASH_INCREMENT;  
        return h;  
    }  
  
    /** 
     * Creates a thread local variable. 
     */  
    public ThreadLocal() {  
    }  
  
    /** 
     * Returns the value in the current thread's copy of this thread-local 
     * variable.  Creates and initializes the copy if this is the first time 
     * the thread has called this method. 
     * 
     * @return the current thread's value of this thread-local 
     */  
    public T get() {  
        Thread t = Thread.currentThread();  
        ThreadLocalMap map = getMap(t);  
        if (map != null)  
            return (T)map.get(this);  
  
        // Maps are constructed lazily.  if the map for this thread  
        // doesn't exist, create it, with this ThreadLocal and its  
        // initial value as its only entry.  
        T value = initialValue();  
        createMap(t, value);  
        return value;  
    }  
  
    /** 
     * Sets the current thread's copy of this thread-local variable 
     * to the specified value.  Many applications will have no need for 
     * this functionality, relying solely on the {@link #initialValue} 
     * method to set the values of thread-locals. 
     * 
     * @param value the value to be stored in the current threads' copy of 
     *        this thread-local. 
     */  
    public void set(T value) {  
        Thread t = Thread.currentThread();  
        ThreadLocalMap map = getMap(t);  
        if (map != null)  
            map.set(this, value);  
        else  
            createMap(t, value);  
    }  
  
    /** 
     * Get the map associated with a ThreadLocal. Overridden in 
     * InheritableThreadLocal. 
     * 
     * @param  t the current thread 
     * @return the map 
     */  
    ThreadLocalMap getMap(Thread t) {  
        return t.threadLocals;  
    }  
  
    /** 
     * Create the map associated with a ThreadLocal. Overridden in 
     * InheritableThreadLocal. 
     * 
     * @param t the current thread 
     * @param firstValue value for the initial entry of the map 
     * @param map the map to store. 
     */  
    void createMap(Thread t, T firstValue) {  
        t.threadLocals = new ThreadLocalMap(this, firstValue);  
    }  
  
    .......  
  
    /** 
     * ThreadLocalMap is a customized hash map suitable only for 
     * maintaining thread local values. No operations are exported 
     * outside of the ThreadLocal class. The class is package private to 
     * allow declaration of fields in class Thread.  To help deal with 
     * very large and long-lived usages, the hash table entries use 
     * WeakReferences for keys. However, since reference queues are not 
     * used, stale entries are guaranteed to be removed only when 
     * the table starts running out of space. 
     */  
    static class ThreadLocalMap {  
  
    ........  
  
    }  
  
}  

ThreadLocal 클래스의 변수는
private final int threadLocalHashCode = nextHashCode();  
private static int nextHashCode = 0;  
private static final int HASH_INCREMENT = 0x61c88647; 

ThreadLocal의 실례적인 변수는threadLocalHashCode 하나,nextHashCode와HASHINCREMENT는 ThreadLocal 클래스의 정적 변수로 실제로HASHINCREMENT는 상량으로 연속적으로 분배된 두 개의 ThreadLocal 실례의threadLocalHashCode 값의 증가량을 나타낸다.nextHashCode는 곧 분배될 다음ThreadLocal 실례의threadLocalHashCode 값을 나타낸다.ThreadLocal 실례인 new ThreadLocal () 을 만들 때 어떤 조작을 했는지 볼 수 있습니다. 구조 함수인 ThreadLocal () 에는 아무런 조작도 없습니다. 유일한 조작은 이 문장입니다.
private final int threadLocalHashCode = nextHashCode(); 

그러면 nextHashCode()는 무엇을 했을까요?
private static synchronized int nextHashCode() {  
    int h = nextHashCode;  
    nextHashCode = h + HASH_INCREMENT;  
    return h;  
} 

ThreadLocal 클래스의 다음hashCode 값인nextHashCode의 값을 실례적인threadLocalHashCode에 부여한 다음,nextHashCode의 값은HASH 를 증가시킨다INCREMENT 이 값입니다.따라서 ThreadLocal 실례의 변수는 이threadLocalHashCode뿐이고final로 서로 다른 ThreadLocal 실례를 구분하는데 사용하는데ThreadLocal류는 주로 도구류로 사용된다면ThreadLocal.set () 이 들어간 대상은 어디에 놓을까요?위의 set () 방법을 보고 두 마디를 합치면
ThreadLocalMap map = Thread.currentThread().threadLocals;

이 ThreadLocalMap 클래스는 ThreadLocal에 정의된 내부 클래스이지만 인스턴스는 Thread 클래스에 사용됩니다.
public class Thread implements Runnable {  
    ......  
  
    /* ThreadLocal values pertaining to this thread. This map is maintained 
     * by the ThreadLocal class. */  
    ThreadLocal.ThreadLocalMap threadLocals = null;    
    ......  
}  

다시 이 문장을 보아라.
if (map != null)  
    map.set(this, value);  

즉, 이 ThreadLocal 실례를 키로 하고 유지할 대상을 값으로 하여 현재 라인의 ThreadLocalMap에 설정합니다. get () 방법 역시 코드를 보면 알 수 있습니다. ThreadLocalMap 종류의 코드가 너무 많아서 저는 댓글을 달지 않고 원본 코드를 보겠습니다.

좋은 웹페이지 즐겨찾기