ThreadLocal執行緒級變數
一、ThreadLocal本地執行緒變數
一般的Web應用劃分為展現層、服務層和持久層三個層次,在不同的層中編寫對應的邏輯,下層通過介面向上層開放功能呼叫。在一般情況下,從接收請求到返回響應所經過的所有程式呼叫都同屬於一個執行緒。
也就是說,同一執行緒貫通N層,不同的執行緒可能由於引數等不同會對程式中的某些變數進行修改,但是又要防止修改後的值對其它執行緒產生影響,因為不同的執行緒可以同時執行滴,這就需要我們解決對某些執行緒共享的變數的訪問衝突問題。ThreadLocal本地執行緒變數就是一種解決方式,它通過將程式中不安全的變數封裝進ThreadLocal中,這相當於為每一個執行緒提供一個獨立的變數副本(其實是不同的物件),執行緒修改變數的值對其它執行緒來說沒影響了,因為其它執行緒有自己的一個副本資訊。
二、藉助ThreadLocal物件每個執行緒只建立一個例項
public static final String dateFormat="yyyy-MM-dd"; private static final ThreadLocal<DateFormat> dfThreadLocal=new ThreadLocal<DateFormat>(){ @Override protected DateFormat initialValue() { return new SimpleDateFormat(dateFormat); } }; public static String dateToString(Date date){ return dfThreadLocal.get().format(date); }
對於每個執行緒,都有一個類似於Map的東西ThreadLocalMap(ThreadLocal的靜態類 ),那它裡面儲存了什麼東東呢,肯定是key-value啊,key就是上面程式碼中的共享靜態變數 dfThreadLocal,value就是DateFormat例項了,即new SimpleDateFormat(dateFormat)這個東東。那接下來,線上程內我要如何去獲取這個值呢,就是靠dfThreadLocal.get()實現滴,方法原始碼如下:
ThreadLocal .ThreadLocalMap inheritableThreadLocals = null ; public T get () { Thread t = Thread.currentThread (); ThreadLocalMap map = getMap(t ); if ( map != null) { ThreadLocalMap.Entry e = map.getEntry (this); if ( e != null) return ( T)e .value; } return setInitialValue (); } ThreadLocalMap getMap (Thread t) { return t .inheritableThreadLocals; }
可以很明顯的看出,首先根據Thread.currentThread ()獲取到inheritableThreadLocals(即ThreadLocalMap,他是Thread的一個變數),然後將this(即最上面程式碼的dfThreadLocal物件)作為key(或索引)獲取到真正的值T(就是SimpleDateFormat物件)啊,至此應該比較清楚了。
為什麼不同的執行緒有各自的值,因為 不同的執行緒--->不同的ThreadLocalMap物件(執行緒的變數)--->通過相同的key(如果有被static修飾)獲取到不同的value值。
備註:一般都被static修飾,因為可以避免在一個執行緒內可能發生的重複建立TSO(Thread Specific Object,即ThreadLocal所關聯的物件),被statis修飾了,同一執行緒key也肯定一樣,value也肯定只有一份了。
一個ThreadLocal例項關聯當前執行緒的一個TSO物件,如果把ThreadLocal宣告為例項變數,那麼每建立一個類例項就會導致一個TSO例項誕生,這肯定沒有這個必要滴。
三、具體使用場景
public class AppMgr {
private static final String module = AppMgr.class.getName();
/**
* 例項
*/
private static AppMgr appMgr = new AppMgr();
/**
* 獲取例項
* @return 例項
*/
public static AppMgr getInstance() {
return appMgr;
}
private static final ThreadLocal TL = new ThreadLocal();
/**
* 獲取執行緒級例項物件
* @return rtnBean rtnBean
*/
private static Bean threadBean() {
Bean bean = (Bean) TL.get();
if (bean == null) {
bean = new Bean();
TL.set(bean);
}
return bean;
}
/**
* 獲取執行緒級例項物件中某引數值
* @param key key
* @return rtnObj rtnObj
*/
public static Object threadVar(String key) {
return threadBean().get(key);
}
/**
* 設定執行緒級例項物件中某引數值
* @param key key
* @param obj setter objInst
*/
public static void setThreadVar(String key, Object obj) {
threadBean().set(key, obj);
}
}
獲取Request例項
/**
* @return Request例項物件
*/
public static HttpServletRequest getInst() {
return (HttpServletRequest) AppMgr.threadVar("request");
}
/**
* TODO$
*
* @param request
*/
public static void setInst(HttpServletRequest request) {
AppMgr.setThreadVar("request", request);
}
獲取值
var strUrl = Leopard.getContextPath() +
"/DoMyServlet?className=ExcelPoiAction&methodName=createExcel&btnCode=empdata&forWard=isFile&namesalt="+namesalt+"&func="+_func
+"&pbean="+encodeURI(encodeURI(strwhere))+"&btnCode"+empexcel;
ifm.src = strUrl;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ExcelPoiAction {
public void createExcel() throws IOException {
HttpServletRequest req = Request.getInst();
this.funcCode = req.getParameter("func"); //功能單元
//this.strWhere = req.getParameter("pbean"); //附件查詢條件
this.strWhere = java.net.URLDecoder.decode(req.getParameter("pbean"),"utf-8");
if (!StringUtils.isEmpty(strWhere)) {
try {
objWhere = new JSONObject("{" + strWhere + "}");
} catch (JSONException e) {}
}
//獲取業務引數
String busiStr = req.getParameter("busiData");
if(!StringUtils.isEmpty(busiStr)){
try {
this.busiData = JsonUtils.transferToBean(busiStr);
} catch (Exception e) {}
}
}
}