往期精選
● 架構師高并發高性能分布式教程(4000G)
● 39階段精品雲計算大資料實戰視訊教程
● 網際網路技術幹貨視訊教程大全【菜單為準】
● 2017年8月最新Intellij IDEA全套視訊教程
● 程式員如何制作高品質的履歷【視訊+履歷】
● 兩套大型電商實戰項目
`
1、資料庫連接配接池實作
Class.forName("com.mysql.jdbc.Driver");
java.sql.Connection conn = DriverManager.getConnection(jdbcUrl);
注意:一次Drivermanager.getConnection(jdbcurl)獲得隻是一個connection,并不能滿足高并發情況。因為connection不是線程安全的,一個connection對應的是一個事物。
每次獲得connection都需要浪費cpu資源和記憶體資源,是很浪費資源的。是以誕生了資料庫連接配接池。資料庫連接配接池實作原理如下:
pool.getConnection(),都是先從threadlocal裡面拿的,如果threadlocal裡面有,則用,保證線程裡的多個dao操作,用的是同一個connection,以保證事務。如果新線程,則将新的connection放在threadlocal裡,再get給到線程。
将connection放進threadlocal裡的,以保證每個線程從連接配接池中獲得的都是線程自己的connection。
Hibernate的資料庫連接配接池源碼實作:

2、有時候ThreadLocal也可以用來避免一些參數傳遞,通過ThreadLocal來通路對象。
比如一個方法調用另一個方法時傳入了8個參數,通過逐層調用到第N個方法,傳入了其中一個參數,此時最後一個方法需要增加一個參數,第一個方法變成9個參數是自然的,但是這個時候,相關的方法都會受到牽連,使得代碼變得臃腫不堪。這時候就可以将要添加的參數設定成線程本地變量,來避免參數傳遞。
上面提到的是ThreadLocal一種亡羊補牢的用途,不過也不是特别推薦使用的方式,它還有一些類似的方式用來使用,就是在架構級别有很多動态調用,調用過程中需要滿足一些協定,雖然協定我們會盡量的通用,而很多擴充的參數在定義協定時是不容易考慮完全的以及版本也是随時在更新的,但是在架構擴充時也需要滿足接口的通用性和向下相容,而一些擴充的内容我們就需要ThreadLocal來做友善簡單的支援。
簡單來說,ThreadLocal是将一些複雜的系統擴充變成了簡單定義,使得相關參數牽連的部分變得非常容易。
3、在某些情況下提升性能和安全。
用SimpleDateFormat這個對象,進行日期格式化。因為建立這個對象本身很費時的,而且我們也知道SimpleDateFormat本身不是線程安全的,也不能緩存一個共享的SimpleDateFormat執行個體,為此我們想到使用ThreadLocal來給每個線程緩存一個SimpleDateFormat執行個體,提高性能。同時因為每個Servlet會用到不同pattern的時間格式化類,是以我們對應每一種pattern生成了一個ThreadLocal執行個體。
public interface DateTimeFormat {
String DATE_PATTERN = "yyyy-MM-dd";
ThreadLocal<DateFormat> DATE_FORMAT = ThreadLocal.withInitial(() -> {
return new SimpleDateFormat("yyyy-MM-dd");
});
String TIME_PATTERN = "HH:mm:ss";
ThreadLocal<DateFormat> TIME_FORMAT = ThreadLocal.withInitial(() -> {
return new SimpleDateFormat("HH:mm:ss");
String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
ThreadLocal<DateFormat> DATE_TIME_FORMAT = ThreadLocal.withInitial(() -> {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
為什麼SimpleDateFormat不安全,可以參考此篇博文:
假如我們把SimpleDateFormat定義成static成員變量,那麼多個thread之間會共享這個sdf對象, 是以Calendar對象也會共享。
假定線程A和線程B都進入了parse(text, pos) 方法, 線程B執行到calendar.clear()後,線程A執行到calendar.getTime(), 那麼就會有問題。
如果不用static修飾,将SimpleDateFormat定義成局部變量: 每調用一次方法就會建立一個SimpleDateFormat對象,方法結束又要作為垃圾回收。加鎖性能較差,每次都要等待鎖釋放後其他線程才能進入。那麼最好的辦法就是:使用ThreadLocal: 每個線程都将擁有自己的SimpleDateFormat對象副本。
附-SimpleDateFormat關鍵源碼:
public class SimpleDateFormat extends DateFormat {
public void parse(String text, ParsePosition pos){
calendar.clear(); // Clears all the time fields
// other logic ...
Date parsedDate = calendar.getTime();
}
}
abstract class DateFormat{
// other logic ...
protected Calendar calendar;
public Date parse(String source) throws ParseException{
ParsePosition pos = new ParsePosition(0);
Date result = parse(source, pos);
if (pos.index == 0)
throw new ParseException("Unparseable date: \"" + source + "\"" ,
pos.errorIndex);
return result;
`