Java多執行緒10:ThreadLocal的作用及使用

五月的倉頡發表於2015-10-04

ThreadLocal的作用

從上一篇對於ThreadLocal的分析來看,可以得出結論:ThreadLocal不是用來解決共享物件的多執行緒訪問問題的,通過ThreadLocal的set()方法設定到執行緒的ThreadLocal.ThreadLocalMap裡的是是執行緒自己要儲存的物件,其他執行緒不需要去訪問,也是訪問不到的。各個執行緒中的ThreadLocal.ThreadLocalMap以及ThreadLocal.ThreadLocal中的值都是不同的物件。

至於為什麼要使用ThreadLocal,不妨這麼考慮這個問題。Java Web中,寫一個Servlet:

public class Servlet extends HttpServlet
{

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
    {
        this.doGet(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
    {
        
    }
}

我在一個普通JavaBean內想拿到這個HttpServletRequest,但是無法通過引數傳遞的方式:

public class OperateRequest
{
    public String operateRequest()
    {
        return null;
    }
}

這時候怎麼辦?第一個解決方案,Servlet類中定義一個全域性的HttpServletRequest,至於怎麼定義就隨便了,可以定義成靜態的,也可以定義成非靜態的但是對外提供setter/getter,然後operateRequest()方法每次都取這個全域性的HttpServletRequest就可以了。

不否認,這是一種可行的解決方案,但是這種解決方案有一個很大的缺點:競爭。既然HttpServletRequest是全域性的,那勢必要引入同步機制來保證執行緒安全性,引入同步機制意味著犧牲響應給使用者的時間----這在注重與使用者之間響應的Java Web中是難以容忍的。

所以,我們引入ThreadLocal,既然ThreadLocal.ThreadLocalMap是執行緒獨有的,別的執行緒訪問不了也沒必要訪問,那我們通過ThreadLocal把HttpServletRequest設定到執行緒的ThreadLocal.ThreadLocalMap裡面去不就好了?這樣,在一次請求中哪裡需要用到HttpServletRequest,就使用ThreadLocal的get()方法就把這個HttpServletRequest給取出來了,是不是一個很好的解決方案呢?

 

ThreadLocal使用

忘記上面那個複雜的問題,我們來看一下ThreadLocal的簡單使用,首先ThreadLocal肯定是全域性共享的:

public class Tools
{
    public static ThreadLocal<String> t1 = new ThreadLocal<String>();
}

寫一個執行緒往ThreadLocal裡面塞值:

public class ThreadLocalThread extends Thread
{
    private static AtomicInteger ai = new AtomicInteger();
    
    public ThreadLocalThread(String name)
    {
        super(name);
    }
    
    public void run()
    {
        try
        {
            for (int i = 0; i < 3; i++)
            {
                Tools.t1.set(ai.addAndGet(1) + "");
                System.out.println(this.getName() + " get value--->" + Tools.t1.get());
                Thread.sleep(200);
            }
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

寫個main函式,啟動三個ThreadLocalThread:

public static void main(String[] args) throws Exception
    {
        ThreadLocalThread a = new ThreadLocalThread("ThreadA");
        ThreadLocalThread b = new ThreadLocalThread("ThreadB");
        ThreadLocalThread c = new ThreadLocalThread("ThreadC");
        a.start();
        b.start();
        c.start();
    }

看一下執行結果:

ThreadA get value--->1
ThreadC get value--->2
ThreadB get value--->3
ThreadB get value--->4
ThreadC get value--->6
ThreadA get value--->5
ThreadC get value--->8
ThreadA get value--->7
ThreadB get value--->9

看到每個執行緒的裡都有自己的String,並且互不影響----因為絕對不可能出現數字重複的情況。用一個ThreadLocal也可以多次set一個資料,set僅僅表示的是執行緒的ThreadLocal.ThreadLocalMap中table的某一位置的value被覆蓋成你最新設定的那個資料而已,對於同一個ThreadLocal物件而言,set後,table中絕不會多出一個資料

 

ThreadLocal再總結

上一篇文章的最後有對ThreadLocal的工作原理進行總結,這裡對ThreadLocal再次進行一個總結:

1、ThreadLocal不是集合,它不儲存任何內容,真正儲存資料的集合在Thread中。ThreadLocal只是一個工具,一個往各個執行緒的ThreadLocal.ThreadLocalMap中table的某一位置set一個值的工具而已

2、同步與ThreadLocal是解決多執行緒中資料訪問問題的兩種思路,前者是資料共享的思路後者是資料隔離的思路

3、同步是一種以時間換空間的思想,ThreadLocal是一種空間換時間的思想

4、ThreadLocal既然是與執行緒相關的,那麼對於Java Web來講,ThreadLocal設定的值只在一次請求中有效,是不是和request很像?因為request裡面的內容也只在一次請求有效,對比一下二者的區別:

(1)ThreadLocal只能存一個值,一個Request由於是Map形式的,可以用key-value形式存多個值

(2)ThreadLocal一般用在框架,Request一般用在表示層、Action、Servlet

相關文章