之前為了方便一個Service下共用List,將List作為類成員變量,當然有點線程安全意識的兄弟們都知道不能就這么直接加在上面。
1 public class PermService { 3 private List<String> list; 5 .... 6 }
當然用鎖(synchronized)又還不至於,於是這里用了線程本地變量ThreadLocal,如下:
1 public class PermService { 2 3 private static final ThreadLocal<List<String>> STR_LIST = new ThreadLocal<List<String>>() { 4 @Override 5 protected List<String> initialValue() { 6 return new ArrayList<String>(); 7 } 8 }; 9 .... 10 }
然后我每次請求進來之后都是類似於這么處理的
1 public void storePermsByRole(String roleId) { 2 List<String> permIds = rolePermMapper.selectPermByRole(roleId); 3 STR_LIST.get().addAll(permIds); 4 }
后面用postman測的時候問題來了:原本和這個roleId沒有關系的permId在返回體里面出現了,查了下庫里面也沒有這兩個關系的記錄,debug斷點打到上面的第3行permIds里面也沒有那一條數據,那么這多出來的一條是哪來的呢?
苦思冥想意識到可能和線程池有關系(這里說的線程池不是你配的全局線程池,也不是手動用Executors生成的線程池),所有線程池一般有個核心線程數,這些線程是不會在一個請求request結束后就銷毀的,而線程本地變量又是依附於線程的(具體可以理解下ThreadLocalMap),所以一個線程上一個請求將數據庫中查出來的list加到線程本地變量后,下一個請求進來原來的list還在里面,從而導致了上面的問題。
這里解決方法是在service中加一段清空ThreadLocal的邏輯,在請求結束之前調一下即可。
1 public void clearThreadLocal() { 2 STR_LIST.remove(); 3 }
ThreadLocal用的守則:
1、請求開始時set
2、請求結束前remove
3、加static關鍵字