談?wù)剬?duì)ThreadLocal的理解

ThreadLocal是什么?

ThreadLocal從字面上的理解是本地線程的意思,然而事實(shí)上它是共享變量的一份拷貝,所以稱之為ThreadLocalvVariable更合適

ThreadLocal對(duì)于多線程數(shù)據(jù)共享的意義?

下面舉個(gè)多線程共享一個(gè)對(duì)象的例子(理解為單例)

package com.test.threadLocal;

public class Student {
    
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }   

}
package com.test.threadLocal;

public class MyThread extends Thread {
    
    private Student student;
    
    private String name;
    
    public MyThread(Student student,String name) {
        this.student = student;
        this.name = name;
    }

    @Override
    public void run() {
       this.student.setName(name);
       String myName = name;
       try
       {
         Thread.sleep(5000);
       }
       catch (InterruptedException e)
       {
         e.printStackTrace();
       }
       System.out.println("Thread: " + Thread.currentThread().getName()
                + ", name: " + student.getName() + ", myName: " + myName);
    }
}
package com.test.threadLocal;

public class Test
{
    public static void main(String[] args)
    {
        Student student = new Student();

        MyThread thread0 = new MyThread(student, "liubei");
        MyThread thread1 = new MyThread(student, "zhangfei");
        MyThread thread2 = new MyThread(student, "guanyu");
        
        thread0.start();
        thread1.start();
        thread2.start();
    }
}

運(yùn)行結(jié)果

Thread: Thread-1, name: zhangfei, myName: zhangfei
Thread: Thread-2, name: zhangfei, myName: guanyu
Thread: Thread-0, name: zhangfei, myName: liubei

通過ThreadLocal改造Student代碼,獲取正確的結(jié)果

package com.test.threadLocal;

public class Student {
    
    private String name;
    
    private static ThreadLocal<String> threadLocal = new ThreadLocal<String>();

    public static String getName() {
        return threadLocal.get();
    }

    public static void setName(String name) {
        threadLocal.set(name);
    }   

}

運(yùn)行結(jié)果

Thread: Thread-2, name: guanyu, myName: guanyu
Thread: Thread-1, name: zhangfei, myName: zhangfei
Thread: Thread-0, name: liubei, myName: liubei

ThreadLocal類中重要的方法

ThreadLocal類的set方法

    / ** Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
        //獲取當(dāng)前線程
        Thread t = Thread.currentThread();
        //ThreadLocalMap是ThreadLocal的一個(gè)靜態(tài)內(nèi)部類,這里從數(shù)據(jù)結(jié)構(gòu)上可以把ThreadLocalMap理解成HashMap,HashMap的解析會(huì)在之后的博客中解釋
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

ThreadLocal類的getMap和createMap方法

ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

Thread類的threadLocals變量

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

ThreadLocal類的get方法

/**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} 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) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

ThreadLocal的setInitialValue,initialValue方法

/**
     * Variant of set() to establish initialValue. Used instead
     * of set() in case user has overridden the set() method.
     *
     * @return the initial value
     */
    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

/**
     * Returns the current thread's "initial value" for this
     * thread-local variable.  This method will be invoked the first
     * time a thread accesses the variable with the {@link #get}
     * method, unless the thread previously invoked the {@link #set}
     * method, in which case the {@code initialValue} method will not
     * be invoked for the thread.  Normally, this method is invoked at
     * most once per thread, but it may be invoked again in case of
     * subsequent invocations of {@link #remove} followed by {@link #get}.
     *
     * <p>This implementation simply returns {@code null}; if the
     * programmer desires thread-local variables to have an initial
     * value other than {@code null}, {@code ThreadLocal} must be
     * subclassed, and this method overridden.  Typically, an
     * anonymous inner class will be used.
     *
     * @return the initial value for this thread-local
     */
//用來設(shè)置初始化的value值,由子類決定是否重寫
protected T initialValue() {
        return null;
    }

對(duì)ThreadLocal,ThreadLocalMap和Thread的關(guān)系的理解

簡單的說每一個(gè)Thread對(duì)應(yīng)的它的一個(gè)ThreadLocalMap,而ThreadLocalMap的key就是ThreadLocal,value是對(duì)應(yīng)存儲(chǔ)的值。從上述原代碼中還可以得知,只要Thread沒銷毀,就能從ThreadLocalMap中g(shù)et到你要的東西,換句話說,我們也可以將我們認(rèn)為重要的信息存進(jìn)去,其實(shí)Struts2中的ActionContext就是這么做的

synchronized和ThreadLocal的區(qū)別

對(duì)于多線程資源共享的問題,synchronized采用了“以時(shí)間換空間”的方式,而ThreadLocal采用了“以空間換時(shí)間”的方式。前者僅提供一份變量,讓不同的線程排隊(duì)訪問,而后者為每一個(gè)線程都提供了一份變量,因此可以同時(shí)訪問而互不影響。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 ThreadLocal很多同學(xué)都搞不懂是什么東西,可以用來干嘛。但面試時(shí)卻又經(jīng)常問到,所以這次我和大家一起學(xué)...
    liangzzz閱讀 12,660評(píng)論 14 228
  • Android Handler機(jī)制系列文章整體內(nèi)容如下: Android Handler機(jī)制1之ThreadAnd...
    隔壁老李頭閱讀 7,851評(píng)論 4 30
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,872評(píng)論 11 349
  • 相關(guān)概念 面向?qū)ο蟮娜齻€(gè)特征 封裝,繼承,多態(tài).這個(gè)應(yīng)該是人人皆知.有時(shí)候也會(huì)加上抽象. 多態(tài)的好處 允許不同類對(duì)...
    東經(jīng)315度閱讀 2,212評(píng)論 0 8
  • 都說沒有雪糕的夏天不是夏天,眼看著就要8月份了!天天牛奶雪糕甜筒的是不是都吃膩了呢,今天小云就帶大家去搜尋香港冰品...
    云游寶閱讀 489評(píng)論 0 1

友情鏈接更多精彩內(nèi)容