【ThreadLocal模式】執行緒上的全域性變數
阿新 • • 發佈:2019-02-04
運用ThreadLocal模式的場景:
1.頻繁建立生命週期短暫的例項(或者例項化代價昂貴)導致效能低下
2.需要執行緒安全,使用‘synchronized’執行緒同步同樣導致效能低下
以下是Tim Cull的博文“SimpleDateFormat: Performance Pig”解決滿足這一需求採用ThreadLocal模式的案列
Tim Cull 寫道:
Tim Cull碰到一個SimpleDateFormat帶來的嚴重的效能問題,該問題主要有SimpleDateFormat引發,建立一個 SimpleDateFormat例項的開銷比較昂貴,解析字串時間時頻繁建立生命週期短暫的例項導致效能低下。即使將 SimpleDateFormat定義為靜態類變數,貌似能解決這個問題,但是SimpleDateFormat是非執行緒安全的,同樣存在問題,如果用 ‘synchronized’執行緒同步同樣面臨問題,同步導致效能下降(執行緒之間序列化的獲取SimpleDateFormat例項)。
Tim Cull使用Threadlocal解決了此問題,對於每個執行緒SimpleDateFormat不存在影響他們之間協作的狀態,為每個執行緒建立一個SimpleDateFormat變數的拷貝或者叫做副本
public class DateUtil { private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; @SuppressWarnings("rawtypes") private static ThreadLocal threadLocal = new ThreadLocal() { protected synchronized Object initialValue() { return new SimpleDateFormat(DATE_FORMAT); } }; public static DateFormat getDateFormat() { return (DateFormat) threadLocal.get(); } public static Date parse(String textDate) throws ParseException { return getDateFormat().parse(textDate); } }
下面做了 靜態計數器(/單例模式) 和 ThreadLocal模式計數器 對比結果,證明ThreadLocal模式下是執行緒安全的,每個執行緒都有自己的獨立副本,執行緒內各個方法及層次都可以使用該變數,而不用再次例項化或者採用傳參【比如Struts的ActionContext】。
public class Counter {
public static Integer number =10;
}
public class LocalCounter { public Integer number =10; private static ThreadLocal<LocalCounter> counter = new ThreadLocal<LocalCounter>(){ protected synchronized LocalCounter initialValue(){ return new LocalCounter(); } };//初始需要覆蓋初始化方法,不覆蓋第一次呼叫get方法值為null,使用前需要先調set方法初始化 public static LocalCounter getCounter() { return (LocalCounter) counter.get(); } public static void setCounter(LocalCounter counterFrom){ counter.set(counterFrom); } }
public class ThreadLocalStub extends Thread {
public void run() {
for (int i = 0; i < 2; i++) {
LocalCounter localCounter = LocalCounter.getCounter();//當前使用時不用再次建立
System.out.println("Thread[" + Thread.currentThread().getName()
+ "],localCounter=" + localCounter.number++);
System.out.println("Thread[" + Thread.currentThread().getName()
+ "],Counter=" + Counter.number++);
LocalCounter.setCounter(localCounter);
}
nextAdd();
}
private void nextAdd(){
LocalCounter localCounter = LocalCounter.getCounter();//當前使用時不用再次建立,執行緒上共享
System.out.println("Thread[" + Thread.currentThread().getName()
+ "],localCounter=" + localCounter.number++);
System.out.println("Thread[" + Thread.currentThread().getName()
+ "],Counter=" + Counter.number++);
LocalCounter.setCounter(localCounter);
}
}
public class ThreadLocalTest {
public static void main(String[] args) {
ThreadLocalStub testThread1 = new ThreadLocalStub();
ThreadLocalStub testThread2 = new ThreadLocalStub();
ThreadLocalStub testThread3 = new ThreadLocalStub();
testThread1.start();
testThread2.start();
testThread3.start();
}
}
執行結果:Thread[Thread-0],localCounter=10
Thread[Thread-1],localCounter=10
Thread[Thread-0],Counter=10
Thread[Thread-1],Counter=11
Thread[Thread-1],localCounter=11
Thread[Thread-1],Counter=12
Thread[Thread-1],localCounter=12
Thread[Thread-1],Counter=13
Thread[Thread-2],localCounter=10
Thread[Thread-2],Counter=14
Thread[Thread-2],localCounter=11
Thread[Thread-2],Counter=15
Thread[Thread-2],localCounter=12
Thread[Thread-2],Counter=16
Thread[Thread-0],localCounter=11
Thread[Thread-0],Counter=17
Thread[Thread-0],localCounter=12
Thread[Thread-0],Counter=18