日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

Java多线程变量共享与隔离

發(fā)布時(shí)間:2024/1/1 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java多线程变量共享与隔离 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • 線程相關(guān)
    • 線程的相關(guān)API
    • 線程的調(diào)度
    • 線程的優(yōu)先級(jí)
  • 方法和變量的線程安全問題
    • 靜態(tài)方法
    • 非靜態(tài)方法
    • 靜態(tài)變量
    • 實(shí)例變量
    • 局部變量
  • 變量共享
    • 共享變量線程安全問題
      • 可見性
      • 可見性舉例
    • 共享變量可見性的實(shí)現(xiàn)
      • synchronized
      • volatile
      • synchronized和volatile比較
      • volatile適用情況
      • 特殊操作會(huì)從主內(nèi)存中拉取值
  • 變量隔離
    • ThreadLocal
      • 使用ThreadLocal的好處
      • ThreadLocal主要方法
      • ThreadLocal源碼分析
      • ThreadLocal注意事項(xiàng)
      • 注意事項(xiàng)


線程相關(guān)

線程的相關(guān)API

  • Thread.currentThread().getName():獲取當(dāng)前線程的名字
  • start():1.啟動(dòng)當(dāng)前線程2.調(diào)用線程中的run方法
  • run():通常需要重寫Thread類中的此方法,將創(chuàng)建的線程要執(zhí)行的操作聲明在此方法中
  • currentThread():靜態(tài)方法,返回執(zhí)行當(dāng)前代碼的線程
  • getName():獲取當(dāng)前線程的名字
  • setName():設(shè)置當(dāng)前線程的名字
  • yield():主動(dòng)釋放當(dāng)前線程的執(zhí)行權(quán)
  • join():在線程中插入執(zhí)行另一個(gè)線程,該線程被阻塞,直到插入執(zhí)行的線程完全執(zhí)行完畢以后,該線程才繼續(xù)執(zhí)行下去
  • stop():過時(shí)方法。當(dāng)執(zhí)行此方法時(shí),強(qiáng)制結(jié)束當(dāng)前線程。
  • sleep(long millitime):線程休眠一段時(shí)間
  • isAlive():判斷當(dāng)前線程是否存活
  • 線程的調(diào)度

    調(diào)度策略:

  • 時(shí)間片:線程的調(diào)度采用時(shí)間片輪轉(zhuǎn)的方式
  • 搶占式:高優(yōu)先級(jí)的線程搶占CPU
  • Java的調(diào)度方法:

  • 對(duì)于同優(yōu)先級(jí)的線程組成先進(jìn)先出隊(duì)列(先到先服務(wù)),使用時(shí)間片策略
  • 對(duì)高優(yōu)先級(jí),使用優(yōu)先調(diào)度的搶占式策略
  • 線程的優(yōu)先級(jí)

    等級(jí):
    MAX_PRIORITY:10
    MIN_PRIORITY:1
    NORM_PRIORITY:5

    方法:
    getPriority():返回線程優(yōu)先級(jí)
    setPriority(int newPriority):改變線程的優(yōu)先級(jí)

    注意事項(xiàng):高優(yōu)先級(jí)的線程要搶占低優(yōu)先級(jí)的線程的cpu的執(zhí)行權(quán)。但是僅是從概率上來說的,高優(yōu)先級(jí)的線程更有可能被執(zhí)行。并不意味著只有高優(yōu)先級(jí)的線程執(zhí)行完以后,低優(yōu)先級(jí)的線程才執(zhí)行。

    方法和變量的線程安全問題

    靜態(tài)方法

    與靜態(tài)成員變量一樣,屬于類本身,在類裝載的時(shí)候被裝載到內(nèi)存(Memory),不自動(dòng)進(jìn)行銷毀,會(huì)一直存在于內(nèi)存中,直到JVM關(guān)閉。

    非靜態(tài)方法

    又叫實(shí)例化方法,屬于實(shí)例對(duì)象,實(shí)例化后才會(huì)分配內(nèi)存,必須通過類的實(shí)例來引用。不會(huì)常駐內(nèi)存,當(dāng)實(shí)例對(duì)象被JVM 回收之后,也跟著消失。

    靜態(tài)變量

    線程非安全,靜態(tài)變量即類變量,位于方法區(qū),為所有該類下的對(duì)象共享,共享一份內(nèi)存,一旦靜態(tài)變量被修改,其他對(duì)象均對(duì)修改可見,故線程非安全。

    實(shí)例變量

    實(shí)例變量為對(duì)象實(shí)例私有,在虛擬機(jī)的堆中分配,若在系統(tǒng)中只存在一個(gè)此對(duì)象的實(shí)例,在多線程環(huán)境下,“猶如”靜態(tài)變量那樣,被某個(gè)線程修改后,其他線程對(duì)修改均可見,故線程非安全;如果每個(gè)線程執(zhí)行都是在不同的對(duì)象中,那對(duì)象與對(duì)象之間的實(shí)例變量的修改將互不影響,故線程安全。

    局部變量

    線程安全,每個(gè)線程執(zhí)行時(shí)將會(huì)把局部變量放在各自棧幀的工作內(nèi)存中,線程間不共享,故不存在線程安全問題。

    變量共享

    共享變量線程安全問題

    可見性

    如果一個(gè)線程對(duì)共享變量值的修改,能夠及時(shí)的被其他線程看到,叫做共享變量的可見性。如果一個(gè)變量同時(shí)在多個(gè)線程的工作內(nèi)存中存在副本,那么這個(gè)變量就叫共享變量。

    Java多線程里對(duì)于共享變量的操作往往需要考慮進(jìn)行一定的同步互斥操作,原來是因?yàn)镴ava內(nèi)存模型導(dǎo)致的共享內(nèi)存對(duì)于線程不可見。

    Java 內(nèi)存模型規(guī)定,將所有的變量都存放在主內(nèi)存中。

  • 線程對(duì)共享變量的所有操作必須在工作內(nèi)存中進(jìn)行,不能直接操作主內(nèi)存。
  • 不同線程間不能訪問彼此的工作內(nèi)存中的變量,線程間變量值的傳遞都必須經(jīng)過主內(nèi)存。

  • 多個(gè)線程同時(shí)對(duì)主內(nèi)存的一個(gè)共享變量進(jìn)行讀取和修改時(shí),首先會(huì)讀取這個(gè)變量到自己的工作內(nèi)存中成為一個(gè)副本,對(duì)這個(gè)副本進(jìn)行改動(dòng)之后,再更新回主內(nèi)存中變量所在的地方。

    由于CPU時(shí)間片是以線程為最小單位,所以這里的工作內(nèi)存實(shí)際上就是指的物理緩存,CPU運(yùn)算時(shí)獲取數(shù)據(jù)的地方;而主內(nèi)存也就是指的是內(nèi)存,也就是原始的共享變量存放的位置。

    一個(gè)線程A對(duì)共享變量1的修改對(duì)線程B可見,需要經(jīng)過下列步驟:

  • 線程A將更改變量1后的值更新到主內(nèi)存
  • 主內(nèi)存將更新后的變量1的值更新到線程B的工作內(nèi)存中變量1的副本
  • 要實(shí)現(xiàn)共享變量的可見性必須保證下列兩點(diǎn):

  • 線程對(duì)工作內(nèi)存中副本的更改能夠及時(shí)的更新到主內(nèi)存上
  • 其他線程能夠及時(shí)的將主內(nèi)存上共享變量的更新刷新到自己工作內(nèi)存的該變量的副本上
  • 可見性舉例

    一個(gè)雙核 CPU 系統(tǒng)架構(gòu),每個(gè)核有自己的控制器和運(yùn)算器,其中控制器包含一組寄存器和操作控制器,運(yùn)算器執(zhí)行算術(shù)邏輔運(yùn)算。CPU的每個(gè)核都有自己的一級(jí)緩存,在有些架構(gòu)里面還有一個(gè)所有CPU都共享的二級(jí)緩存。

    1、線程A首先獲取共享變量X的值,由于兩級(jí)Cache都沒有命中,所以加載主內(nèi)存中X的值,假如為0。然后把X=0的值緩存到兩級(jí)緩存,線程A修改X的值為1,然后將其寫入兩級(jí)Cache,并且刷新到主內(nèi)存。線程A操作完畢后,線程A所在的CPU的兩級(jí)Cache內(nèi)和主內(nèi)存里面的X的值都是l。

    2、線程B獲取X的值,首先一級(jí)緩存沒有命中,然后看二級(jí)緩存,二級(jí)緩存命中了,所以返回X=1;到這里一切都是正常的,因?yàn)檫@時(shí)候主內(nèi)存中也是X=l。然后線程B修改X的值為2,并將其存放到線程2所在的一級(jí)Cache和共享二級(jí)Cache中,最后更新主內(nèi)存中X的值為2,到這里一切都是好的。

    3、線程A這次又需要修改X的值,獲取時(shí)一級(jí)緩存命中,并且X=l這里問題就出現(xiàn)了,明明線程B已經(jīng)把X的值修改為2,為何線程A獲取的還是l呢?這就是共享變量的內(nèi)存不可見問題,也就是線程B寫入的值對(duì)線程A不可見。

    共享變量可見性的實(shí)現(xiàn)

    Java中可以通過synchronized、volatile、java concurrent類來實(shí)現(xiàn)共享變量的可見性。

    synchronized

    使用synchronized可以保證原子性(synchronized代碼塊內(nèi)容要么不執(zhí)行,要執(zhí)行就保證全部執(zhí)行完畢)和可見性,修改后的代碼為在write和read方法上加synchronized關(guān)鍵字。

    JMM關(guān)于Synchronized的兩條規(guī)定:

  • 線程解鎖前,必須把共享變量的最新值刷新到主內(nèi)存中;
  • 線程加鎖時(shí),將清空工作內(nèi)存中共享變量的值,從而使用共享變量是需要從主內(nèi)存中重新讀取最新的值(加鎖與解鎖需要統(tǒng)一把鎖)。
  • synchronized 實(shí)際上是對(duì)訪問修改共享變量的代碼塊進(jìn)行加互斥鎖,多個(gè)線程對(duì)synchronized代碼塊的訪問時(shí),某一時(shí)刻僅僅有一個(gè)線程在訪問和修改代碼塊中的內(nèi)容(加鎖),其他所有的線程等待該線程離開代碼塊時(shí)(釋放鎖)才有機(jī)會(huì)進(jìn)入synchronized代碼塊。

    某一個(gè)線程進(jìn)入synchronized代碼塊前后,執(zhí)行過程入如下:

  • 線程獲得互斥鎖
  • 清空工作內(nèi)存
  • 從主內(nèi)存拷貝共享變量最新的值到工作內(nèi)存成為副本
  • 執(zhí)行代碼
  • 將修改后的副本的值刷新回主內(nèi)存中
  • 線程釋放鎖
  • 隨后,其他代碼在進(jìn)入synchronized代碼塊的時(shí)候,所讀取到的工作內(nèi)存上共享變量的值都是上一個(gè)線程修改后的最新值。

    注意,synchronized加鎖后用到的變量才會(huì)從主內(nèi)存拉取、才會(huì)修改后刷新回主內(nèi)存。

    舉例說明:

    該代碼程序不會(huì)進(jìn)入死循環(huán),分析執(zhí)行過程:

  • 創(chuàng)建o對(duì)象;
  • 創(chuàng)建新線程,異步執(zhí)行;
  • 執(zhí)行while,此時(shí)isStop=false;進(jìn)入循環(huán);
  • 遇到synchronized鎖,得到鎖;
  • 清空主線程的工作內(nèi)存;
    從主內(nèi)存拷貝共享變量最新的值到工作內(nèi)存成為副本(該步驟省略,因?yàn)樗锩鏇]有代碼塊(不知道是否可以這么理解));
    執(zhí)行代碼(該步驟省略,因?yàn)闆]有代碼);
    將修改后的副本的值刷新會(huì)主內(nèi)存中(該步驟省略,因?yàn)闆]有代碼);
  • 釋放鎖
  • 重新執(zhí)行while,此時(shí)工作內(nèi)存中沒有isStop,從主內(nèi)存中獲取最新值,如果isStop=false,繼續(xù)執(zhí)行4、5、6、7步驟,知道2秒鐘后子線程將isStop值改為true,并刷新回主線程,此時(shí)步驟7從主內(nèi)存中獲得的isStop=true,結(jié)束while循環(huán),主線程結(jié)束。
  • 特別情況:

    在上個(gè)例子的基礎(chǔ)上加上一段代碼:System.out.println(isStop),查看打印日志:

    沒有達(dá)到自己預(yù)期的效果:結(jié)束前一次貌似應(yīng)該打印true;
    上面代碼其實(shí)有兩種結(jié)果:

  • 最后一次打印isStop為true(幾率小)
  • 最后一次打印isStop為false(幾率大)
    具體原因:

    System.out.println(boolean x)方法其實(shí)也含有一個(gè)synchronized鎖。
    結(jié)果1出現(xiàn)的情況:where時(shí),isStop=false,第一個(gè)鎖o對(duì)象鎖清空工作內(nèi)存后重新從內(nèi)存得到isStop=true(幾率小)
    結(jié)果2出現(xiàn)的情況:where時(shí),isStop=false,第一個(gè)鎖o對(duì)象鎖清空工作內(nèi)存后重新從內(nèi)存得到isStop=false,println得到的參數(shù)值為false(值傳遞),println的鎖PrintStream對(duì)象鎖清空工作內(nèi)存,再次where時(shí),isStop=true(幾率大)。
  • volatile

    volatile變量每次被線程訪問時(shí),都強(qiáng)迫線程從主內(nèi)存中重讀該變量的最新值,而當(dāng)該變量發(fā)生修改變化時(shí),也會(huì)強(qiáng)迫線程將最新的值刷新回主內(nèi)存。這樣一來,不同的線程都能及時(shí)的看到該變量的最新值。

    但是volatile不能保證變量更改的原子性
    比如number++,這個(gè)操作實(shí)際上是三個(gè)操作的集合(讀取number,number加1,將新的值寫回number),volatile只能保證每一 步的操作對(duì)所有線程是可見的,但是假如兩個(gè)線程都需要執(zhí)行number++,那么這一共6個(gè)操作集合,之間是可能會(huì)交叉執(zhí)行的,那么最后導(dǎo)致number 的結(jié)果可能會(huì)不是所期望的。

    舉例說明:

    程序不會(huì)進(jìn)入死循環(huán),原因:
    isStop是被volatile修飾的,所有每次while時(shí)都是從主內(nèi)存中獲取isStop的值,當(dāng)子線程2秒鐘后修改了isStop的值為true,并刷新進(jìn)了住內(nèi)存,此后while從主內(nèi)存中獲取的isStop值為true,結(jié)束循環(huán)。

    AtomicInteger:一個(gè)提供原子操作的Integer的類。在Java語言中,++i和i++操作并不是線程安全的,在使用的時(shí)候,不可避免的會(huì)用到synchronized關(guān)鍵字。而AtomicInteger則通過一種線程安全的加減操作接口。

    synchronized和volatile比較

  • volatile不需要同步操作,所以效率更高,不會(huì)阻塞線程,但是適用情況比較窄

  • volatile讀變量相當(dāng)于加鎖(即進(jìn)入synchronized代碼塊),而寫變量相當(dāng)于解鎖(退出synchronized代碼塊)

  • synchronized既能保證共享變量可見性,也可以保證鎖內(nèi)操作的原子性;volatile只能保證可見性

  • volatile適用情況

  • 對(duì)變量的寫入操作不依賴當(dāng)前值
    比如自增自減、number = number + 5等(不滿足)

  • 當(dāng)前volatile變量不依賴于別的volatile變量
    比如 volatile_var > volatile_var2這個(gè)不等式(不滿足)

  • 特殊操作會(huì)從主內(nèi)存中拉取值

  • 變量被賦值
  • 變量參與計(jì)算(比如加減乘除)
  • 字符串拼接(驗(yàn)證過)
  • ++、–等操作
  • 線程休眠(驗(yàn)證過)
  • 以上操作會(huì)從主內(nèi)存拉去值到工作內(nèi)存,所有如果在上面的例子的weile循環(huán)中有這些操作,不會(huì)造成死循環(huán)。

    變量隔離

    多線程之間就是因?yàn)閿?shù)據(jù)共享在多個(gè)線程才導(dǎo)致了線程不安全,這就要求線程間的數(shù)據(jù)需要隔離,從根本上解決了線程安全問題。

    ThreadLocal

    提供線程局部變量;一個(gè)線程局部變量在多個(gè)線程中,分別有獨(dú)立的值(副本)。

    使用ThreadLocal的好處

  • 達(dá)到線程安全
  • 不需要加鎖,提高執(zhí)行效率
  • 更高效地利用內(nèi)存節(jié)省開銷,用ThreadLocal可以節(jié)省內(nèi)存和開銷。
  • 免去傳參的繁瑣,不需要每次都傳同樣的參數(shù),ThreadLocal使得代碼耦合度更低,更優(yōu)雅
  • ThreadLocal主要方法

    主要是initialValue、set、get、remove這幾個(gè)方法

    • initialValue方法返回當(dāng)前線程對(duì)應(yīng)的“初始值”,這是一個(gè)延遲加載的方法,只有在調(diào)用get的時(shí)候,才會(huì)觸發(fā)。
    • 當(dāng)線程第一次使用get方法訪問變量時(shí),將調(diào)用initialValue方法,除非線程先前調(diào)用了set方法,在這種情況下,不會(huì)為線程調(diào)用本initialValue方法。
    • 通常,每個(gè)線程最多調(diào)用一次initialValue()方法,但如果已經(jīng)調(diào)用了一次remove()后,再調(diào)用get(),則可以再次調(diào)用initialValue(),相當(dāng)于第一次調(diào)用get()。
    • 如果不重寫initialValue()方法,這個(gè)方法會(huì)返回null。一般使用匿名內(nèi)部類的方法來重寫initialValue()方法,以便在后續(xù)使用中可以初始化副本對(duì)象。

    ThreadLocal源碼分析

    Thread、ThreadLocal、ThreadLocalMap三者的關(guān)系:

    每個(gè)Thread對(duì)象都有一個(gè)ThreadLocalMap,每個(gè)ThreadLocalMap可以存儲(chǔ)多個(gè)ThreadLocal。

    get方法
    get方法是先取出當(dāng)前線程的ThreadLocalMap,然后調(diào)用map.getEntry方法,把本ThreadLocal的引用作為參數(shù)傳入,取出map中屬于本ThreadLocal的value。

    注意:這個(gè)map以及map中的key和value都是保存在線程中ThreadLocalMap的,而不是保存在ThreadLocal中

    getMap方法:
    獲取到當(dāng)前線程內(nèi)的ThreadLocalMap對(duì)象。每個(gè)線程內(nèi)都有ThreadLocalMap對(duì)象,名為threadLocals,初始值為null。

    set方法
    把當(dāng)前線程需要全局共享的value傳入

    initialValue方法
    這個(gè)方法沒有默認(rèn)實(shí)現(xiàn),如果要用initialValue方法,需要自己實(shí)現(xiàn),通常使用匿名內(nèi)部類的方式實(shí)現(xiàn)

    remove方法
    刪除對(duì)應(yīng)這個(gè)線程的值。

    ThreadLocalMap類
    ThreadLocalMap類,也就是Thread.threadLocals。

    // 此行聲明在Thread類中,創(chuàng)建ThreadLocalMap就是對(duì)Thread類的這個(gè)成員變量賦值 ThreadLocal.ThreadLocalMap threadLocals = null;

    ThreadLocalMap 類是每個(gè)線程Thread類里面的變量,但ThreadLocalMap這個(gè)靜態(tài)內(nèi)部類定義在ThreadLocal類中,其中發(fā)現(xiàn)這一行代碼

    private Entry[] table;

    里面最重要的是一個(gè)鍵值對(duì)數(shù)組Entry[] table,可以認(rèn)為是一個(gè)map,鍵值對(duì):

    • 鍵:這個(gè)ThreadLocal
    • 值:實(shí)際需要的成員變量,比如User或者SimpleDateFormat對(duì)象

    這個(gè)思路和HashMap一樣,那么我們可以把它想象成HashMap來分析,但是實(shí)現(xiàn)上略有不同。

    比如處理沖突方式不同,HashMap采用鏈地址法,而ThreadLocalMap采用的是線性探測(cè)法,也就是如果發(fā)生沖突,就繼續(xù)找下一個(gè)空位置,而不是用鏈表拉鏈

    通過源碼分析可以看出,setInitialValue和直接set最后都是利用map.set()方法來設(shè)置值,最后都會(huì)對(duì)應(yīng)到ThreadLocalMap的一個(gè)Entry

    ThreadLocal注意事項(xiàng)

    1.ThreadLocal內(nèi)存泄漏問題
    ThreadLocalMap中的Entry繼承自 WeakReference,是弱引用,ThreadLocal可能出現(xiàn)Value泄漏!

    什么是內(nèi)存泄漏:某個(gè)對(duì)象不再有用,但是占用的內(nèi)存卻不能被回收

    弱引用:通過WeakReference類實(shí)現(xiàn)的,在GC的時(shí)候,不管內(nèi)存空間足不足都會(huì)回收這個(gè)對(duì)象,適用于內(nèi)存敏感的緩存,ThreadLocal中的key就用到了弱引用,有利于內(nèi)存回收。
    強(qiáng)引用:我們平日里面的用到的new了一個(gè)對(duì)象就是強(qiáng)引用,例如 Object obj = new Object();當(dāng)JVM的內(nèi)存空間不足時(shí),寧愿拋出OutOfMemoryError使得程序異常終止也不愿意回收具有強(qiáng)引用的存活著的對(duì)象。

    ThreadLocalMap 的每個(gè) Entry 都是一個(gè)對(duì)key的弱引用,同時(shí),每個(gè) Entry 都包含了一個(gè)對(duì)value的強(qiáng)引用,如下:

    static class Entry extends WeakReference<ThreadLocal<?>> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k); // key值給WeakReference處理value = v; // value直接用變量保存,是強(qiáng)引用}}

    正常情況下,當(dāng)線程終止,保存在ThreadLocalMap里的value會(huì)被垃圾回收,因?yàn)闆]有任何強(qiáng)引用了。但如果線程不終止(比如線程需要保持很久),那么key對(duì)應(yīng)的value就不能被回收,因?yàn)橛幸韵碌恼{(diào)用鏈:

    Thread---->ThreadLocalMap---->Entry(key為null,弱引用被回收)---->value

    因?yàn)関alue和Thread之間還存在這個(gè)強(qiáng)引用鏈路,所以導(dǎo)致value無法回收,就可能會(huì)出現(xiàn)OOM。

    JDK已經(jīng)考慮到了這個(gè)問題,所以在set, remove, rehash方法中會(huì)掃描key為null的Entry,并把對(duì)應(yīng)的value設(shè)置為null,這樣value對(duì)象就可以被回收。比如rehash里面調(diào)用resize,如果key回收了,那么value也設(shè)置為null,斷開強(qiáng)引用鏈路,便于垃圾回收。

    private void resize() {......省略代碼ThreadLocal<?> k = e.get();if (k == null) {e.value = null; // Help the GC} ......

    但是如果一個(gè)ThreadLocal不被使用,那么實(shí)際上set, remove, rehash方法也不會(huì)被調(diào)用,如果同時(shí)線程又不停止,那么調(diào)用鏈就一直存在,那么就導(dǎo)致了value的內(nèi)存泄漏。

    ThreadLocal如何避免內(nèi)存泄漏
    及時(shí)調(diào)用remove方法,就會(huì)刪除對(duì)應(yīng)的Entry對(duì)象,可以避免內(nèi)存remove泄漏,所以使用完ThreadLocal之后,應(yīng)該調(diào)用remove方法。
    比如攔截器獲取到用戶信息,用戶信息存在ThreadLocalMap中,線程請(qǐng)求結(jié)束之前攔住它,并用remove清除User對(duì)象,這樣就能穩(wěn)妥的保證不會(huì)內(nèi)存泄漏。

    注意事項(xiàng)

  • 共享對(duì)象問題
    如果在每個(gè)線程中ThreadLocal.set()進(jìn)去的東西本來就是多線程共享的同一個(gè)對(duì)象,比如static對(duì)象,那么多個(gè)線程的ThreadLocal.get()取得的還是這個(gè)共享對(duì)象本身,還是有并發(fā)訪問問題。
  • 不要強(qiáng)行使用ThreadLocal
    如果可以不使用ThreadLocal就能解決問題,那么不要強(qiáng)行使用,在任務(wù)數(shù)很少的時(shí)候,可以通過在局部變量中新建對(duì)象解決。
  • 優(yōu)先使用框架的支持,而不是自己創(chuàng)造
    在Spring中,如果可以使用RequestContextHolder,那么就不需要自己維護(hù)ThreadLocal,因?yàn)樽约嚎赡軙?huì)忘記調(diào)用remove()方法等,造成內(nèi)存泄漏。
  • 參考文章:
    Java多線程里共享變量線程安全問題的原因
    Java多線程共享變量控制
    Java多線程超詳解
    ThreadLocal詳解

    總結(jié)

    以上是生活随笔為你收集整理的Java多线程变量共享与隔离的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。