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