ThreadLocal类以及应用技巧
我們完全可以用ThreadLocal來完成線程范圍內(nèi)數(shù)據(jù)的共享。
package com.learn.day05.demo05;import java.util.Random;public class ThreadScopeShareData {// 定義一個(gè)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里面里面扔數(shù)據(jù)即可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());//直接取,不用什么關(guān)鍵字,它直接從當(dāng)前線程中取}}static class TestB {public void getData() {System.out.println("B get data from " + Thread.currentThread().getName() + ": " + threadLocal.get());//直接取,不用什么關(guān)鍵字,它直接從當(dāng)前線程中取}} }可以看出,其實(shí)ThreadLocal就相當(dāng)于一個(gè)Map,只不過我們不需要設(shè)定key了,它默認(rèn)就是當(dāng)前的Thread,往里面放數(shù)據(jù),直接set即可,取數(shù)據(jù),直接get即可,很方便,就不用Map一個(gè)個(gè)存了,但是問題來了,ThreadLocal雖然存取方便,但是get()方法中根本沒有參數(shù),也就是說我們只能往ThreadLocal中放一個(gè)數(shù)據(jù),多了就不行了,那么該如何解決這個(gè)問題呢?
很明顯,ThreadLocal是個(gè)容器,且只能存一下,那么如果有多個(gè)數(shù)據(jù),我們可以定義一個(gè)類,把數(shù)據(jù)都封裝到這個(gè)類中,然后扔到ThreadLocal中,用的時(shí)候取這個(gè)類,再?gòu)念愔腥ノ覀兿胍臄?shù)據(jù)即可。
好,現(xiàn)在有兩個(gè)線程,每個(gè)線程都要操作各自的數(shù)據(jù),而且數(shù)據(jù)有兩個(gè):名字和年齡。根據(jù)上面的思路,寫一個(gè)demo,如下:
?
這樣進(jìn)行一下封裝就可以實(shí)現(xiàn)多個(gè)數(shù)據(jù)的存儲(chǔ)了,但是上面這個(gè)程序是不太好的,原因很明顯,在線程中,我要自己new一個(gè)對(duì)象,然后對(duì)其進(jìn)行操作,最后還得把這個(gè)對(duì)象扔到當(dāng)前線程中。這不太符合設(shè)計(jì)的思路,設(shè)計(jì)的思路應(yīng)該是這樣的,不能讓用戶自己去new啊,如果有個(gè)類似于getThreadInstance()的方法,用戶想要從ThreadLocal中拿什么對(duì)象就用該對(duì)象去調(diào)用這個(gè)getThreadInstance()方法多好,這樣拿到的永遠(yuǎn)都是本線程范圍內(nèi)的對(duì)象了。
這讓我想到了學(xué)習(xí)JDBC的時(shí)候,從ThreadLocal中拿connection時(shí)的做法了,如果當(dāng)前ThreadLocal中有就拿出來,沒有就產(chǎn)生一個(gè),這跟這里的需求是一樣的,我想要一個(gè)User,那我應(yīng)該用User去調(diào)用getThreadLInstance()方法獲取本線程中的一個(gè)User對(duì)象,如果有就拿,如果沒有就產(chǎn)生一個(gè)。完全一樣的思路。這個(gè)設(shè)計(jì)跟單例的模式有點(diǎn)像,這里說有點(diǎn)像不是本質(zhì)上像,是代碼結(jié)構(gòu)很像。先看一下簡(jiǎn)單的單例模式代碼結(jié)構(gòu):
?
這是懶漢式單例模式的代碼結(jié)構(gòu),我門完全可以效仿該思路去設(shè)計(jì)一個(gè)從當(dāng)前線程中拿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去調(diào)用getThreadLocal這個(gè)靜態(tài)方法獲取本線程范圍內(nèi)的一個(gè)User對(duì)象//這里就優(yōu)雅多了,我完全不用關(guān)心如何去拿該線程中的對(duì)象,如何把對(duì)象放到threadLocal中//我只要拿就行,而且拿出來的肯定就是當(dāng)前線程中的對(duì)象,原因看下面User類中的設(shè)計(jì)User2.getThreadInstance().setName("name" + data);User2.getThreadInstance().setAge(data);new TestA().getData();new TestB().getData();}}).start();}}static class TestA {public void getData() {//還是調(diào)用這個(gè)靜態(tài)方法拿,因?yàn)閯倓傄呀?jīng)拿過一次了,threadLocal中已經(jīng)有了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>();//注意,這不是單例,每個(gè)線程都可以new,所以不用synchronized,//但是每個(gè)threadLocal中是單例的,因?yàn)橛辛说脑捑筒粫?huì)再new了public static /*synchronized*/ User2 getThreadInstance() {User2 instance = threadLocal.get(); //先從當(dāng)前threadLocal中拿if(instance == null) {instance = new User2();threadLocal.set(instance);//如果沒有就新new一個(gè)放到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;} }經(jīng)過這樣的改造,代碼就優(yōu)雅多了,外界從來不要考慮如何去當(dāng)前線程中拿數(shù)據(jù),只要拿就行,拿出來的肯定就是當(dāng)前線程中你想要的對(duì)象,因?yàn)樵趯?duì)象內(nèi)部已經(jīng)寫好了這個(gè)靜態(tài)方法了,而且拿出來操作完了后,也不需要再放到threadLocal中,因?yàn)樗緛砭驮趖hreadLocal中,這就封裝的相當(dāng)好了。
總結(jié)
以上是生活随笔為你收集整理的ThreadLocal类以及应用技巧的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mybatis和ehcache整合
- 下一篇: 为什么要使用缓存