1. 程式人生 > >【ThreadLocal模式】執行緒上的全域性變數

【ThreadLocal模式】執行緒上的全域性變數

運用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