Java併發——線程局部變量

1. 爲什麼要使用線程局部變量你?

如果創建的對象實現了Runnable接口的類的實例,用它作爲傳入參數,並創建多個線程對象並啓動這些線程,那麼所有的線程將共享相同的屬性。如果在一個線程中改變一個屬性,所有線程都會被這個改變影響。這樣,不得不處理同步的問題。

要想把線程私有數據(如一個用戶ID)和線程關聯起來,可以使用線程局部變量 java.lang.ThreadLocal類

2. 什麼是線程局部變量?

每個ThreadLocal的實例代表了一個線程局部變量,它爲每一條訪問線程提供了單獨的存儲槽(storage slot)。可以把它想象成具有多個槽的變量,然後每條線程可以在同一個變量中存儲不同的值。並且,每條線程都只能 看到自己的值,而不會意識到其他線程在這個變量中也有自己的值。

聲明方式:ThreadLocal<T>

3.構造函數和方法:

(1)ThreadLocal(): 創建了一個新的線程局部變量。

(2)T get(): 返回調用線程的存儲槽中的值。如果當這個線程調用此方法時,值不存在,那麼get()會調用 initialValue()方法。

(3)T initialValue(): 創建調用線程的存儲槽並存入一個初始(默認)值。默認的初始值是null。

(4)void remove(): 清空調用線程的存儲槽。在沒有set()方法介入的情況下,如果緊隨此方法之後調用get()方法,那麼get()方法就會直接調用initialValue()。

(5)void set(T value):設置調用線程的存儲槽上的值。

//使用線程局部變量關聯不同用戶的ID

public class ThreadLocalDemo {

	private static volatile ThreadLocal<String> userID = new ThreadLocal<>();
	
	public static void main(String[] args)
	{
		Runnable r = new Runnable()
				{
			         @Override
			         public void run()
			         {
			        	 String name = Thread.currentThread().getName();
			        	 if(name.equals("A"))
			        		 userID.set("foxtrot");
			        	 else
			        		 userID.set("charlie");
			        		 
			        	 System.out.println(name + " " + userID.get());
			         }
				};
				
		Thread t1 = new Thread(r, "A");
		Thread t2 = new Thread(r, "B");
		t1.start();
		t2.start();
	}
}

運行結果:

A foxtrot
B charlie

4. 存儲在線程局部變量中的值都是相關的。當一個新的線程被創建出來,它會獲得一個新的包含initialValue()值的存儲槽。

當想要把值從父類傳給子線程時,可以使用InheritableThreadLocal。

InheritableThreadLocal是ThreadLocal的子類,除了InheritableThreadLocal()構造方法,還有:

T childValue(T parentValue):在子線程被創建出來的時候,用父線程的值(即參數)計算出子線程的初始值。


//將一個對象從父線程傳到子線程
public class InheritableThreadLocalDemo {

	private static volatile InheritableThreadLocal<Integer> intVal = new InheritableThreadLocal<Integer>();
	
	public static void main(String[] args)
	{
		Runnable rParent = new Runnable()
				{
			         @Override
			         public void run()
			         {
			        	 intVal.set(10);  //父線程在intVal中存儲一個值爲10的Integer
			        	 Runnable rChild = new Runnable()
			        			 {
			        		          @Override
			        		          public void run()
			        		          {
			        		        	  String name = Thread.currentThread().getName();
				        		           System.out.printf("%s %d%n",name,intVal.get());
			        		          }			        		  	 
			        			 };
			        	Thread thdChild = new Thread(rChild,"Child");
			        	thdChild.start();
			        	
			         }
				};
				
				new Thread(rParent).start();
	}
}

主線程創建了一條父線程,這條線程在intVal中存儲了一個值爲10的java.lang.Integer對象。父線程之後創建了一條子線程,這條線程訪問intVal並取得父線程中的Integer對象。 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章