1. 程式人生 > >ThreadLocal執行緒級變數

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) {}
}
}
}