ThreadLocal的使用

考慮下面的代碼,假設有兩個線程同時經過下面的兩個方法對同一個對象df進行訪問,因爲DateFormat不是線程安全的,因此有可能形成數據錯亂,返回非你指望的結果(你想要當前的日期,返回的確實1970年1月1號)。java

private DateFormat df = new SimpleDateFormat("MM/dd/yy");

	public String formatCurrentDate() {
		return df.format(new Date());
	}

	public String formatFirstOfJanyary1970() {
		return df.format(new Date(0));
	}


使用傳統的同步方法能夠很好得避免多線程併發訪問時的數據錯亂,可是會形成線程阻塞,從而嚴重影響性能。仔細考慮這裏的df的使用場景,它並不須要各個線程保持一致,也就是說不是多線程通訊的手段,所以能夠在各個線程中各有一個‘副本’ , 這種狀況下就能夠使用ThreadLocal變量。編程

private DateFormat df = new SimpleDateFormat("MM/dd/yy");

	public synchronized String formatCurrentDate() {
		return df.format(new Date());
	}

	public synchronized String formatFirstOfJanyary1970() {
		return df.format(new Date(0));
	}


改寫後的用法,去掉了同步方法,這時不一樣的線程訪問df時, 它們使用的同一個df, 可是經過df.get() 獲得的是不一樣的DateFormat對象, 從而保證各個線程格式化日期時互不干擾:安全

public static ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
		protected DateFormat initialValue() {
			return new SimpleDateFormat("MM/dd/yy");
		}
	};

	public String formatCurrentDate() {
		return df.get().format(new Date());
	}

	public String formatFirstOfJanyary1970() {
		return df.get().format(new Date(0));
	}


使用ThreadLocal要注意內存泄露的狀況。考慮上面的情景,若是咱們使用的不是DateFormat對象,而是自定義的類對象,那麼這個對象可能持有應用程序context或其餘資源的引用。同時這個對象在不一樣的線程各有一份,若是有某個線程在線程池中(不正常地)存活了很長時間,那麼就可能形成內存泄露了。內存泄露的解決能夠經過良好的編程習慣、使用WeakReference等手段解決。多線程


參考:https://plumbr.eu/blog/when-and-how-to-use-a-threadlocal併發

相關文章
相關標籤/搜索