线程系列5--java中的ThreadLocal类实现线程范围内的数据共享(二)
ThreadLocal類可以理解成一個類似與map集合使用,以當前線程當做key 來使用,將線程氛圍內需要共享的數據當做value,形成鍵值對的形式使用。ThreadLocal和線程同步機制都是為了解決多線程中對同一個變量的訪問沖突問題。
在同步機制中,通過對象的鎖機制保證同一時間只有一個線程訪問變量。這時該變量是多個線程共享的,使用同步機制要求程序慎密地分析什么時候對變量進行讀寫,什么時候需要鎖定某個對象,什么時候釋放對象鎖等繁雜的問題,程序設計和編寫難度相對較大。而ThreadLocal則從另一個角度來解決多線程的并發訪問。ThreadLocal會為每一個線程提供一個獨立的變量副本,從而隔離了多個線程對數據的訪問沖突。因為每一個線程都擁有自己的變量副本,從而也就沒有必要對該變量進行同步了。ThreadLocal提供了線程安全的共享對象,在編寫多線程代碼時,可以把不安全的變量封裝進ThreadLocal。概括起來說,對于多線程資源共享的問題,同步機制采用了“以時間換空間”的方式,而ThreadLocal采用了“以空間換時間”的方式。前者僅提供一份變量,讓不同的線程排隊訪問,而后者為每一個線程都提供了一份變量,因此可以同時訪問而互不影響。
我還是以傳智播客中的代碼舉例說明:下面代碼是利用ThreadLocal來實現多線程的數據共享,代碼很簡單不做過多講解。
/*** ThreadLocal 類似于map集合 只是集合的key是當前正在運行的線程 * 通過ThreadLocal可以將變量(或者是變量的容器)與當前線程相綁定.*/private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() ;public static void main(String[] args) {for(int i = 0 ;i<2 ;i++){new Thread(new Runnable(){@Overridepublic void run() {int data = new Random().nextInt();threadLocal.set(data) ; //將數據綁定到帶當前線程System.out.println(Thread.currentThread().getName()+ " put random data:"+data);new A().get() ;new B().get() ;}}).start() ;}}static class A {public void get(){//取數據都從當前線程中取得 int d = threadLocal.get() ;System.out.println("A from " + Thread.currentThread().getName() + " get data :" + d);}}static class B{public void get(){//取數據都從當前線程中取得 int d = threadLocal.get() ;System.out.println("B from " + Thread.currentThread().getName() + " get data :" + d);}}當線程中要存放多個對象時怎么辦那,顯然我們要封裝對象,將對象放進ThreadLocal中,此處不再書寫相關代碼了,我們只需要將封裝的對象直接當做泛型放到ThreadLocal中即可,非常易懂;
代碼繼續升級改造,傳智播客中是利用單例模式的思想,將我們要封裝的對象提出來給與ThreadLocal封裝。不過我覺得張孝祥老師在講解時使用單例模式不太好,單例模式是在系統中只有一份,但是ThreadLocal是每個線程復制一個對象,是典型的利用空間換時間,每個線程都會對應一個封裝的對象,并不想單例模式那樣。
private static ThreadLocal<MyThreadLocal> threadLocal = new ThreadLocal<MyThreadLocal>();public static void main(String[] args) {for(int i = 0 ;i<2 ;i++){new Thread(new Runnable(){@Overridepublic void run() {int data = new Random().nextInt();MyThreadLocal.getMyThreadLocalInstant().setName("傲視蒼穹--大潑猴-孫悟空"+data);MyThreadLocal.getMyThreadLocalInstant().setValue("傲視蒼穹--大潑猴-楊嬋"+data);System.out.println(Thread.currentThread().getName()+ " put random data:"+data);new A().get() ;new B().get() ;}}).start() ;}}static class A {public void get(){//取數據都從當前線程中取得 MyThreadLocal myThreadLocal = MyThreadLocal.getMyThreadLocalInstant();System.out.println("A from " + Thread.currentThread().getName() + " name :" + myThreadLocal.getName()+"value:"+myThreadLocal.getValue());}}static class B{public void get(){//取數據都從當前線程中取得 MyThreadLocal myThreadLocal = MyThreadLocal.getMyThreadLocalInstant();System.out.println("B from " + Thread.currentThread().getName() + " name :" + myThreadLocal.getName()+"value:"+myThreadLocal.getValue());}}static class MyThreadLocal{private MyThreadLocal(){}//單例模式:線程安全 // private static MyThreadLocal myThreadLocal = null; // public static synchronized MyThreadLocal getMyThreadLocalInstant(){ // if(myThreadLocal == null){ // myThreadLocal = new MyThreadLocal(); // } // return myThreadLocal; // }//使用單例模式的思想,來實現對象創建的思想private static ThreadLocal<MyThreadLocal> map = new ThreadLocal<>();public static MyThreadLocal getMyThreadLocalInstant(){if(map.get() == null){map.set(new MyThreadLocal());}return map.get();}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getValue() {return value;}public void setValue(String value) {this.value = value;}private String name;private String value;}上述代碼利用單例模式的思想來管理ThreadLocal對象的創建,但是這樣并不是說整個內存中只有一個對象,大家千萬理解清楚,我聽視頻的時候就被這點迷惑了,這里只是用單例模式的思想來管理ThreadLocal對象的創建,每個線程都會創建一個被共享的數據對象放到ThreadLocal中。
下面內容摘自知乎用戶,鏈接:https://www.zhihu.com/question/23089780/answer/62097840
可以總結為一句話:ThreadLocal的作用是提供線程內的局部變量,這種變量在線程的生命周期內起作用,減少同一個線程內多個函數或者組件之間一些公共變量的傳遞的復雜度。
舉個例子,我出門需要先坐公交再做地鐵,這里的坐公交和坐地鐵就好比是同一個線程內的兩個函數,我就是一個線程,我要完成這兩個函數都需要同一個東西:公交卡(北京公交和地鐵都使用公交卡),那么我為了不向這兩個函數都傳遞公交卡這個變量(相當于不是一直帶著公交卡上路),我可以這么做:將公交卡事先交給一個機構,當我需要刷卡的時候再向這個機構要公交卡(當然每次拿的都是同一張公交卡)。這樣就能達到只要是我(同一個線程)需要公交卡,何時何地都能向這個機構要的目的。
有人要說了:你可以將公交卡設置為全局變量啊,這樣不是也能何時何地都能取公交卡嗎?但是如果有很多個人(很多個線程)呢?大家可不能都使用同一張公交卡吧(我們假設公交卡是實名認證的),這樣不就亂套了嘛。現在明白了吧?這就是ThreadLocal設計的初衷:提供線程內部的局部變量,在本線程內隨時隨地可取,隔離其他線程。
轉載于:https://www.cnblogs.com/aoshicangqiong/p/7733179.html
總結
以上是生活随笔為你收集整理的线程系列5--java中的ThreadLocal类实现线程范围内的数据共享(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 软件测试作业1 -- 关于c++项目中类
- 下一篇: $.ajax使用总结(一):Form提交