<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          如何解決ThreadLocal因線程復(fù)用導(dǎo)致失效的問題?

          共 3927字,需瀏覽 8分鐘

           ·

          2020-08-18 07:15


          ThreadLocal 提供一個(gè)線程本地變量, 顧名思義, 這個(gè)變量值與線程相關(guān), 不同線程之間互不影響.


          它是如何做到這點(diǎn)的呢? 我們來看下源碼.


          public?class?ThreadLocal {
          ????protected?T initialValue()?{
          ????????return?null;
          ????}

          ????public?T get()?{
          ????????Thread t = Thread.currentThread();
          ????????ThreadLocalMap map?= getMap(t);
          ????????if?(map?!= null) {
          ????????????ThreadLocalMap.Entry e = map.getEntry(this);
          ????????????if?(e != null) {
          ????????????????@SuppressWarnings("unchecked")
          ????????????????T result = (T)e.value;
          ????????????????return?result;
          ????????????}
          ????????}
          ????????return?setInitialValue();
          ????}

          ????private?T setInitialValue()?{
          ????????T value = initialValue();
          ????????Thread t = Thread.currentThread();
          ????????ThreadLocalMap map?= getMap(t);
          ????????if?(map?!= null)
          ????????????map.set(this, value);
          ????????else
          ????????????createMap(t, value);
          ????????return?value;
          ????}

          ????public?void?set(T value)?{
          ????????Thread t = Thread.currentThread();
          ????????ThreadLocalMap map?= getMap(t);
          ????????if?(map?!= null)
          ????????????map.set(this, value);
          ????????else
          ????????????createMap(t, value);
          ????}

          ?????public?void?remove()?{
          ?????????ThreadLocalMap m = getMap(Thread.currentThread());
          ?????????if?(m != null)
          ?????????????m.remove(this);
          ?????}

          ????ThreadLocalMap getMap(Thread t)?{
          ????????return?t.threadLocals;
          ????}

          ????void?createMap(Thread t, T firstValue)?{
          ????????t.threadLocals = new?ThreadLocalMap(this, firstValue);
          ????}
          }

          public?class?Thread?implements?Runnable?{

          ????/* ThreadLocal values pertaining to this thread. This map is maintained
          ?????* by the ThreadLocal class. */

          ????ThreadLocal.ThreadLocalMap threadLocals = null;
          }


          上面只截取了一部分源碼, 我們主要看get(), set()和remove()方法. 當(dāng)我們調(diào)用get()方法時(shí), 首先獲取當(dāng)前線程, 然后獲取該線程的 ThreadLocalMap 對象, 如果 ThreadLocalMap 為null, 則調(diào)用初始化方法, 否則從 ThreadLocalMap 中獲取值. 當(dāng)我們調(diào)用set()方法時(shí), 和get()方法步驟類似, 會(huì)把值設(shè)置到當(dāng)前線程的 ThreadLocalMap 對象中.


          通過看源碼我們知道, get()和set()方法的第一步都是獲取當(dāng)前線程的 ThreadLocalMap 對象, 且值是存儲(chǔ)在這個(gè)對象中的, 所以我們說 ThreadLocal 提供一個(gè)線程本地變量, 這個(gè)變量值與線程相關(guān), 不同線程之間互不影響.


          remove()方法用來清除這個(gè)線程本地變量的值, 這個(gè)方法很容易被忽視, 其實(shí)我們在進(jìn)行g(shù)et()和set()操作之后, 需要調(diào)用remove方法來清除這個(gè)線程本地變量的值, 否則會(huì)因線程復(fù)用導(dǎo)致ThreadLocal失效. 我們來看下面兩個(gè)的代碼:


          (1) 代碼一


          public?class?Test{

          ????public?static?void?main(String[] args) {
          ????????final ThreadLocal dataSourceTypes = new?ThreadLocal(){
          ????????????@Override
          ????????????protected?String initialValue()
          {
          ????????????????return?"master";
          ????????????}
          ????????};

          ????????Executor executor = Executors.newFixedThreadPool(5);
          ????????for?(int?i = 0; i < 5; i++) {
          ????????????executor.execute(new?Runnable() {
          ????????????????@Override
          ????????????????public?void?run()
          {
          ????????????????????System.out.println(dataSourceTypes.get());
          ????????????????????dataSourceTypes.set("slave");
          ????????????????}
          ????????????});
          ????????}
          ????}
          }
          結(jié)果:
          master
          master
          master
          master
          master


          (2) 代碼二


          public?class?Test{

          ????public?static?void?main(String[] args) {
          ????????final ThreadLocal dataSourceTypes = new?ThreadLocal(){
          ????????????@Override
          ????????????protected?String initialValue()
          {
          ????????????????return?"master";
          ????????????}
          ????????};

          ????????Executor executor = Executors.newFixedThreadPool(5);
          ????????for?(int?i = 0; i < 8; i++) {
          ????????????executor.execute(new?Runnable() {
          ????????????????@Override
          ????????????????public?void?run()
          {
          ????????????????????System.out.println(dataSourceTypes.get());
          ????????????????????dataSourceTypes.set("slave");
          ????????????????}
          ????????????});
          ????????}
          ????}
          }

          結(jié)果:
          master
          master
          master
          master
          slave
          master
          slave
          slave

          我們對比下上面兩個(gè)代碼, 代碼一的開啟的線程數(shù)與線程池中的線程數(shù)相等, 得到了我們想要的結(jié)果. 而代碼二開啟的線程數(shù)大于線程池中的線程數(shù), 導(dǎo)致結(jié)果與我們期望的不一樣, 為什么會(huì)出現(xiàn)這種情況呢?


          看了ThreadLocal的源碼之后, 我們知道ThreadLocal是與線程相關(guān)的, 代碼二中線程池里只有5個(gè)線程, 而我們開啟了8個(gè)線程, 這就會(huì)導(dǎo)致線程復(fù)用, 在第一次調(diào)用get()方法時(shí)發(fā)現(xiàn)當(dāng)前線程的ThreadLocalMap里有值, 從而導(dǎo)致不會(huì)調(diào)用setInitialValue()方法.


          這種線程復(fù)用的問題在實(shí)際開發(fā)中會(huì)出現(xiàn)嗎? 當(dāng)然, 比如我們的Tomcat服務(wù)器里存在一個(gè)線程池, 對于每一個(gè)http請求, 都會(huì)從線程池中取出一個(gè)空閑線程, 但這個(gè)線程池的默認(rèn)線程只有75個(gè), 超過后一樣會(huì)出現(xiàn)線程復(fù)用.


          所以為了解決這種線程復(fù)用的問題, 我們在開發(fā)中使用ThreadLocal的最后要調(diào)用remove()方法.


          原文鏈接:csdn.net/litianxiang_kaola/article/details/90169600




          瀏覽 85
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  婷婷精品免费久久 | 欧美中文字幕在线播放 | 成人日本一区 | 亚洲殴洲国产黄片 | 骚逼影院|