轉自:http://blog.csdn.net/sundacheng1989/article/details/11647441html
在Java中有一種ThreadLocal機制,爲每個使用該變量的線程都提供一個變量值的副本,是每個線程均可以獨立地改變本身的副本,而不會和其它線程的副本衝突。從線程的角度看,就好像每個線程都徹底擁有該變量。好比在hibernate中使用Session的時候,由於Session是線程不安全的,因此要考慮併發問題。而使用ThreadLocal的話,會在每一個線程中有一個Session的副本,因此就不會有線程衝突的問題。java
.NET中也有相應的機制,來實現變量的線程局部化,並且有多種方法。安全
1. 使用ThreadStatic特性併發
ThreadStatic特性是最簡單的TLS使用,且只支持靜態字段,只須要在字段上標記這個特性就能夠了:ide
-
- [ThreadStatic]
- static string str = "hehe";
-
- static void Main()
- {
-
- Thread th = new Thread(() => { str = "Mgen"; Display(); });
- th.Start();
- th.Join();
- Display();
- }
-
- static void Display()
- {
- Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, str);
- }
運行結果:函數
1 hehe
3 Mgenurl
能夠看到,str靜態字段在兩個線程中都是獨立存儲的,互相不會被修改。spa
2. 使用命名的LocalDataStoreSlot類型.net
顯然ThreadStatic特性只支持靜態字段太受限制了。.NET線程類型中的LocalDataStoreSlot提供更好的TLS支持。咱們先來看看命名的LocalDataStoreSlot類型,能夠經過Thread.AllocateNamedDataSlot來分配一個命名的空間,經過Thread.FreeNamedDataSlot來銷燬一個命名的空間。空間數據的獲取和設置則經過Thread類型的GetData方法和SetData方法。hibernate
- static void Main()
- {
-
- LocalDataStoreSlot slot = Thread.AllocateNamedDataSlot("slot");
-
- Thread.SetData(slot, "hehe");
-
- Thread th = new Thread(() =>
- {
- Thread.SetData(slot, "Mgen");
- Display();
- });
-
- th.Start();
- th.Join();
- Display();
-
- Thread.FreeNamedDataSlot("slot");
- }
-
-
- static void Display()
- {
- LocalDataStoreSlot dataslot = Thread.GetNamedDataSlot("slot");
- Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, Thread.GetData(dataslot));
- }
輸出:
3 Mgen
1 hehe
3. 使用未命名的LocalDataStoreSlot類型
線程一樣支持未命名的LocalDataStoreSlot,未命名的LocalDataStoreSlot不須要手動清除,分配則須要Thread.AllocateDataSlot方法。注意因爲未命名的LocalDataStoreSlot沒有名稱,所以沒法使用Thread.GetNamedDataSlot方法,只能在多個線程中引用同一個LocalDataStoreSlot才能夠對TLS空間進行操做,將上面的命名的LocalDataStoreSlot代碼改爲未命名的LocalDataStoreSlot執行:
-
- static LocalDataStoreSlot slot;
-
- static void Main()
- {
-
- slot = Thread.AllocateDataSlot();
-
- Thread.SetData(slot, "hehe");
-
- Thread th = new Thread(() =>
- {
- Thread.SetData(slot, "Mgen");
- Display();
- });
-
- th.Start();
- th.Join();
- Display();
- }
-
-
- static void Display()
- {
- Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, Thread.GetData(slot));
- }
-
4. 使用.NET 4.0的ThreadLocal<T>類型
.NET 4.0在線程方面加入了不少東西,其中就包括ThreadLocal<T>類型,他的出現更大的簡化了TLS的操做。ThreadLocal<T>類型和Lazy<T>驚人類似,構造函數參數是Func<T>用來建立對象(固然也能夠理解成對象的默認值),而後用Value屬性來獲得或者設置這個對象。ThreadLocal的操做或多或少有點像上面的未命名的LocalDataStoreSlot,但ThreadLocal感受更簡潔更好理解。
- static ThreadLocal<string> local;
-
- static void Main()
- {
-
- local = new ThreadLocal<string>(() => "hehe");
-
- Thread th = new Thread(() =>
- {
- local.Value = "Mgen";
- Display();
- });
-
- th.Start();
- th.Join();
- Display();
- }
-
-
- static void Display()
- {
- Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, local.Value);
- }
輸出:
3 Mgen
1 hehe
5. 強調一下不一樣方法和TLS的默認值
上面代碼都是一個一個線程設置值,另外一個線程直接修改值而後輸出,不會覺察到TLS中默認值的情況,下面專門強調一下不一樣方法的默認值情況。ThreadStatic不提供默認值:
- [ThreadStatic]
- static int i = 123;
-
- static void Main()
- {
-
- Console.WriteLine(i);
-
- ThreadPool.QueueUserWorkItem(_ => Console.WriteLine(i));
-
- Console.ReadKey();
- }
輸出:
123
0
顯然本地線程TLS數據時123,而靜態變量的默認值不會在另外一個線程中初始化的。
LocalDataStoreSlot很容易能夠看出來,不可能有默認值,由於初始化只能構造一個空間,而不能賦予它值,Thread.SetData顯然只會在TLS中設置數據,仍是用代碼演示一下:
- static LocalDataStoreSlot slot = Thread.AllocateDataSlot();
-
- static void Main()
- {
- Thread.SetData(slot, 123);
-
- Console.WriteLine(Thread.GetData(slot));
-
- ThreadPool.QueueUserWorkItem(_ => Console.WriteLine(Thread.GetData(slot) == null));
-
- Console.ReadKey();
- }
輸出:
123
True
第二行是True,那麼另外一個線程中的數據是null。
最後重點:.NET 4.0後的ThreadLocal會提供默認值的,還記得我上面說的那句話「ThreadLocal的操做或多或少有點像上面的未命名的LocalDataStoreSlot」?有人可能會問那爲何要創造出ThreadLocal?還有一個很大的區別ThreadLocal能夠提供TLS中數據的默認值。(另外還有ThreadLocal是泛型類,而LocalDataStoreSlot不是)。
- static ThreadLocal<int> local = new ThreadLocal<int>(() => 123);
- static void Main()
- {
-
- Console.WriteLine(local.Value);
-
- ThreadPool.QueueUserWorkItem(_ => Console.WriteLine(local.Value));
-
- Console.ReadKey();
- }
輸出:
123
123
在Java中有一種ThreadLocal機制,爲每個使用該變量的線程都提供一個變量值的副本,是每個線程均可以獨立地改變本身的副本,而不會和其它線程的副本衝突。從線程的角度看,就好像每個線程都徹底擁有該變量。好比在hibernate中使用Session的時候,由於Session是線程不安全的,因此要考慮併發問題。而使用ThreadLocal的話,會在每一個線程中有一個Session的副本,因此就不會有線程衝突的問題。
.NET中也有相應的機制,來實現變量的線程局部化,並且有多種方法。
1. 使用ThreadStatic特性
ThreadStatic特性是最簡單的TLS使用,且只支持靜態字段,只須要在字段上標記這個特性就能夠了:
-
- [ThreadStatic]
- static string str = "hehe";
-
- static void Main()
- {
-
- Thread th = new Thread(() => { str = "Mgen"; Display(); });
- th.Start();
- th.Join();
- Display();
- }
-
- static void Display()
- {
- Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, str);
- }
運行結果:
1 hehe
3 Mgen
能夠看到,str靜態字段在兩個線程中都是獨立存儲的,互相不會被修改。
2. 使用命名的LocalDataStoreSlot類型
顯然ThreadStatic特性只支持靜態字段太受限制了。.NET線程類型中的LocalDataStoreSlot提供更好的TLS支持。咱們先來看看命名的LocalDataStoreSlot類型,能夠經過Thread.AllocateNamedDataSlot來分配一個命名的空間,經過Thread.FreeNamedDataSlot來銷燬一個命名的空間。空間數據的獲取和設置則經過Thread類型的GetData方法和SetData方法。
- static void Main()
- {
-
- LocalDataStoreSlot slot = Thread.AllocateNamedDataSlot("slot");
-
- Thread.SetData(slot, "hehe");
-
- Thread th = new Thread(() =>
- {
- Thread.SetData(slot, "Mgen");
- Display();
- });
-
- th.Start();
- th.Join();
- Display();
-
- Thread.FreeNamedDataSlot("slot");
- }
-
-
- static void Display()
- {
- LocalDataStoreSlot dataslot = Thread.GetNamedDataSlot("slot");
- Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, Thread.GetData(dataslot));
- }
輸出:
3 Mgen
1 hehe
3. 使用未命名的LocalDataStoreSlot類型
線程一樣支持未命名的LocalDataStoreSlot,未命名的LocalDataStoreSlot不須要手動清除,分配則須要Thread.AllocateDataSlot方法。注意因爲未命名的LocalDataStoreSlot沒有名稱,所以沒法使用Thread.GetNamedDataSlot方法,只能在多個線程中引用同一個LocalDataStoreSlot才能夠對TLS空間進行操做,將上面的命名的LocalDataStoreSlot代碼改爲未命名的LocalDataStoreSlot執行:
-
- static LocalDataStoreSlot slot;
-
- static void Main()
- {
-
- slot = Thread.AllocateDataSlot();
-
- Thread.SetData(slot, "hehe");
-
- Thread th = new Thread(() =>
- {
- Thread.SetData(slot, "Mgen");
- Display();
- });
-
- th.Start();
- th.Join();
- Display();
- }
-
-
- static void Display()
- {
- Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, Thread.GetData(slot));
- }
-
4. 使用.NET 4.0的ThreadLocal<T>類型
.NET 4.0在線程方面加入了不少東西,其中就包括ThreadLocal<T>類型,他的出現更大的簡化了TLS的操做。ThreadLocal<T>類型和Lazy<T>驚人類似,構造函數參數是Func<T>用來建立對象(固然也能夠理解成對象的默認值),而後用Value屬性來獲得或者設置這個對象。ThreadLocal的操做或多或少有點像上面的未命名的LocalDataStoreSlot,但ThreadLocal感受更簡潔更好理解。
- static ThreadLocal<string> local;
-
- static void Main()
- {
-
- local = new ThreadLocal<string>(() => "hehe");
-
- Thread th = new Thread(() =>
- {
- local.Value = "Mgen";
- Display();
- });
-
- th.Start();
- th.Join();
- Display();
- }
-
-
- static void Display()
- {
- Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, local.Value);
- }
輸出:
3 Mgen
1 hehe
5. 強調一下不一樣方法和TLS的默認值
上面代碼都是一個一個線程設置值,另外一個線程直接修改值而後輸出,不會覺察到TLS中默認值的情況,下面專門強調一下不一樣方法的默認值情況。ThreadStatic不提供默認值:
- [ThreadStatic]
- static int i = 123;
-
- static void Main()
- {
-
- Console.WriteLine(i);
-
- ThreadPool.QueueUserWorkItem(_ => Console.WriteLine(i));
-
- Console.ReadKey();
- }
輸出:
123
0
顯然本地線程TLS數據時123,而靜態變量的默認值不會在另外一個線程中初始化的。
LocalDataStoreSlot很容易能夠看出來,不可能有默認值,由於初始化只能構造一個空間,而不能賦予它值,Thread.SetData顯然只會在TLS中設置數據,仍是用代碼演示一下:
- static LocalDataStoreSlot slot = Thread.AllocateDataSlot();
-
- static void Main()
- {
- Thread.SetData(slot, 123);
-
- Console.WriteLine(Thread.GetData(slot));
-
- ThreadPool.QueueUserWorkItem(_ => Console.WriteLine(Thread.GetData(slot) == null));
-
- Console.ReadKey();
- }
輸出:
123
True
第二行是True,那麼另外一個線程中的數據是null。
最後重點:.NET 4.0後的ThreadLocal會提供默認值的,還記得我上面說的那句話「ThreadLocal的操做或多或少有點像上面的未命名的LocalDataStoreSlot」?有人可能會問那爲何要創造出ThreadLocal?還有一個很大的區別ThreadLocal能夠提供TLS中數據的默認值。(另外還有ThreadLocal是泛型類,而LocalDataStoreSlot不是)。
- static ThreadLocal<int> local = new ThreadLocal<int>(() => 123);
- static void Main()
- {
-
- Console.WriteLine(local.Value);
-
- ThreadPool.QueueUserWorkItem(_ => Console.WriteLine(local.Value));
-
- Console.ReadKey();
- }
輸出:
123
123