ThreadLocal类以及应用技巧
我們完全可以用ThreadLocal來完成線程范圍內數據的共享。
package com.learn.day05.demo05;import java.util.Random;public class ThreadScopeShareData {// 定義一個ThreadLocalprivate static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();public static void main(String[] args) {for(int i = 0; i < 2; i ++) {new Thread(new Runnable() {@Overridepublic void run() {int data = new Random().nextInt();System.out.println(Thread.currentThread().getName() + " has put a data: " + data);threadLocal.set(data);//直接往threadLocal里面里面扔數據即可new TestA().getData();new TestB().getData();}}).start();}}static class TestA {public void getData() {System.out.println("A get data from " + Thread.currentThread().getName() + ": " + threadLocal.get());//直接取,不用什么關鍵字,它直接從當前線程中取}}static class TestB {public void getData() {System.out.println("B get data from " + Thread.currentThread().getName() + ": " + threadLocal.get());//直接取,不用什么關鍵字,它直接從當前線程中取}} }可以看出,其實ThreadLocal就相當于一個Map,只不過我們不需要設定key了,它默認就是當前的Thread,往里面放數據,直接set即可,取數據,直接get即可,很方便,就不用Map一個個存了,但是問題來了,ThreadLocal雖然存取方便,但是get()方法中根本沒有參數,也就是說我們只能往ThreadLocal中放一個數據,多了就不行了,那么該如何解決這個問題呢?
很明顯,ThreadLocal是個容器,且只能存一下,那么如果有多個數據,我們可以定義一個類,把數據都封裝到這個類中,然后扔到ThreadLocal中,用的時候取這個類,再從類中去我們想要的數據即可。
好,現在有兩個線程,每個線程都要操作各自的數據,而且數據有兩個:名字和年齡。根據上面的思路,寫一個demo,如下:
?
這樣進行一下封裝就可以實現多個數據的存儲了,但是上面這個程序是不太好的,原因很明顯,在線程中,我要自己new一個對象,然后對其進行操作,最后還得把這個對象扔到當前線程中。這不太符合設計的思路,設計的思路應該是這樣的,不能讓用戶自己去new啊,如果有個類似于getThreadInstance()的方法,用戶想要從ThreadLocal中拿什么對象就用該對象去調用這個getThreadInstance()方法多好,這樣拿到的永遠都是本線程范圍內的對象了。
這讓我想到了學習JDBC的時候,從ThreadLocal中拿connection時的做法了,如果當前ThreadLocal中有就拿出來,沒有就產生一個,這跟這里的需求是一樣的,我想要一個User,那我應該用User去調用getThreadLInstance()方法獲取本線程中的一個User對象,如果有就拿,如果沒有就產生一個。完全一樣的思路。這個設計跟單例的模式有點像,這里說有點像不是本質上像,是代碼結構很像。先看一下簡單的單例模式代碼結構:
?
這是懶漢式單例模式的代碼結構,我門完全可以效仿該思路去設計一個從當前線程中拿User的辦法,所以將程序修改如下
package com.learn.day05.demo05;import java.util.Random;public class ThreadScopeShareData5 { //不需要在外面定義threadLocal了,放到User類中了 // private static ThreadLocal<User> threadLocal = new ThreadLocal<User>();public static void main(String[] args) {for(int i = 0; i < 2; i ++) {new Thread(new Runnable() {@Overridepublic void run() {int data = new Random().nextInt();System.out.println(Thread.currentThread().getName() + " has put a data: " + data);//這里直接用User去調用getThreadLocal這個靜態方法獲取本線程范圍內的一個User對象//這里就優雅多了,我完全不用關心如何去拿該線程中的對象,如何把對象放到threadLocal中//我只要拿就行,而且拿出來的肯定就是當前線程中的對象,原因看下面User類中的設計User2.getThreadInstance().setName("name" + data);User2.getThreadInstance().setAge(data);new TestA().getData();new TestB().getData();}}).start();}}static class TestA {public void getData() {//還是調用這個靜態方法拿,因為剛剛已經拿過一次了,threadLocal中已經有了User2 user = User2.getThreadInstance();System.out.println("A get data from " + Thread.currentThread().getName() + ": "+ user.getName() + "," + user.getAge());}}static class TestB {public void getData() {User2 user = User2.getThreadInstance();System.out.println("A get data from " + Thread.currentThread().getName() + ": "+ user.getName() + "," + user.getAge());}}}class User2 {private User2() {}private static ThreadLocal<User2> threadLocal = new ThreadLocal<User2>();//注意,這不是單例,每個線程都可以new,所以不用synchronized,//但是每個threadLocal中是單例的,因為有了的話就不會再new了public static /*synchronized*/ User2 getThreadInstance() {User2 instance = threadLocal.get(); //先從當前threadLocal中拿if(instance == null) {instance = new User2();threadLocal.set(instance);//如果沒有就新new一個放到threadLocal中}return instance; //向外返回該User}private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;} }經過這樣的改造,代碼就優雅多了,外界從來不要考慮如何去當前線程中拿數據,只要拿就行,拿出來的肯定就是當前線程中你想要的對象,因為在對象內部已經寫好了這個靜態方法了,而且拿出來操作完了后,也不需要再放到threadLocal中,因為它本來就在threadLocal中,這就封裝的相當好了。
總結
以上是生活随笔為你收集整理的ThreadLocal类以及应用技巧的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mybatis和ehcache整合
- 下一篇: 为什么要使用缓存