Java多線程10:ThreadLocal的作用及使用


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM