threadLocal遇上線程池導致局部變量變化


這兩天一直在查無線app一個詭異的問題,表象是stg的接口返回數據,和線上接口的返回數據不一致。

 

1、初步判斷:有緩存,查看代碼后發現緩存時間直郵6分鍾,而且同一個接口,其他調用方的返回數據,stg和線上是保持一致的。

 

2、確認版本后,把線上版本和stg環境的版本號,進行多次check,發現版本是一致的。

 

3、線上和stg接口的返回數據,來源於我依賴的接口,現在接口stg和線上是不一致,而不是一個有數據一個沒數據,判斷是調用了不同的接口。了解下來接口會根據不同的版本號返回不同的數據,所以判斷有版本控制的appClientVersion這個字段傳的不對,安裝最新的app包,debug我們的stg環境發現版本是4.0.3沒有傳錯。在各種解釋不通的情況下,我只好加上日志,把輸入輸出打出來。

 

 

上線后查看日志發現:我的屌絲android手機居然變成了iphone,版本號也是4.0.1,起初懷疑無線版本號不對,連上Fiddler,並切換線上和stg環境,發現請求的clientInfo沒有錯,的確是android ,4.0.3的版本,那問題肯定是venus到我們的服務再到我們調用服務之前clientInfo被改動了。查看代碼發現,clientInfo信息是從ThreadLocal里面拿的。。。原來拿的是別的線程的內容,怪不得連屌絲機都能升級成高富帥。這就可以解釋為什么stg永遠好的,線上有問題,因為stg測試的全是4.0.3版本的發布包測的。

 

我們的版本控制是控制interfaceVersion來控制的,再拿到ThreadLocal里面的內容的時候,我們重新賦值了,所以,這個參數沒有問題,而appClientVersion和ClientSystem都沒有重新賦值,拿到別的線程的內容后就變成了我們所依賴的接口的老版本,所以返回了不同的數據。

ThreadLocal可以為當前線程保存局部變量,而InheritableThreadLocal則可以在創建子線程的時候將父線程的局部變量傳遞到子線程中。

 如果使用了線程池(如Executor),那么即使即使父線程已經結束,子線程依然存在並被池化。這樣,線程池中的線程在下一次請求被執行的時候,ThreadLocal對象的get()方法返回的將不是當前線程中設定的變量,因為池中的“子線程”根本不是當前線程創建的,當前線程設定的ThreadLocal變量也就無法傳遞給線程池中的線程。

[java]  view plain  copy
 
  1. import java.util.concurrent.Executor;  
  2. importjava.util.concurrent.Executors;  
  3.    
  4. public classThreadLocalTest {  
  5.    
  6.     private staticThreadLocal<String> vLocal = newThreadLocal<String>();  
  7.    
  8.     public static voidmain(String[] args) {  
  9.    
  10.         Executorexecutor = Executors.newFixedThreadPool(2);  
  11.    
  12.         // 模擬10個請求  
  13.         for (int i =0; i < 10; i++) {  
  14.            final int flag= i;  
  15.            executor.execute(new Runnable() {  
  16.    
  17.                 @Override  
  18.                public voidrun() {  
  19. //                   vLocal.set(null);  
  20.                  //模擬某一線程改變了ThreadLocal的值  
  21.                    if (flag == 1) {  
  22.                        vLocal.set("set:test");  
  23.                    }  
  24.                    System.out.println(Thread.currentThread().getName()+ ":" + vLocal.get());  
  25.                }  
  26.            });  
  27.        }  
  28.    
  29.     }  
  30. }  



pool-1-thread-1:null

pool-1-thread-2:set:test

pool-1-thread-1:null

pool-1-thread-2:set:test

pool-1-thread-1:null

pool-1-thread-2:set:test

pool-1-thread-1:null

pool-1-thread-2:set:test

pool-1-thread-1:null
pool-1-thread-2:set:test

因此,必須將外部線程中的ThreadLocal變量顯式地傳遞給線程池中的線程,或者每個請求來的時候先threadLocal.set(null)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM