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