Thread-Specific Storage Pattern
什么是Thread-Specific Storage Pattern?
現(xiàn)在有一個保管箱,許多投幣保管箱都排在一起,每個人拿著自己的要是進(jìn)來了,退出的時候拿著自己的行李。又有一個人拿著自己的鑰匙進(jìn)來了,打開自己的保管箱,拿走自己的行李,而不會破壞其他人的行李。
這個模式名稱翻譯成中文就是“線程獨有的儲藏庫”。這個模式只有一個入口,但是內(nèi)部對每個線程提供特有的存儲空間。
首先介紹以下java.lang.ThreadLocal類
java.lang.ThreadLocal的實例可以想象成一種集合架構(gòu)(collection)。也就是說,ThreadLocal的實例只有一個,但是可以管理多個對象。因為ThreadLocal的實例管理了多個對象,所以ThreadLocal擁有存放(set)和取得(get)方法。
public void set()
ThreadLocal類的實例有一個set方法,可以將參數(shù)指定的實例存放到調(diào)用set方法的線程所對應(yīng)的存儲空間。這里存放的實例,可以調(diào)用get方法取得。這個set方法是沒有用傳入?yún)?shù)的,而是檢查當(dāng)前線程,也就是Thread.currentThread()的值,自動以這個值作為key存放實例,相當(dāng)于把自己的行李放進(jìn)保管箱一樣。
public Object get()
ThreadLocal類的get方法,可以調(diào)用get方法的線程對應(yīng)的實例(現(xiàn)在線程對應(yīng)的實例)。之前set的實例,就是現(xiàn)在get的返回值。如果沒有set過,就返回null。調(diào)用get時,相當(dāng)于從自己的保管箱拿出行李一樣。與set方法一樣,get方法沒有表示線程的參數(shù),因為程序會在get里自己去檢查現(xiàn)在的線程,也就是自己本身就是鍵值。
下面設(shè)想一種情況,我們設(shè)置3個線程,每個線程負(fù)責(zé)對一個不同的文件進(jìn)行寫入。利用ThreadLocal,我們給每個線程設(shè)置獨立的文件名稱屬性,使得線程得以對不同文件進(jìn)行寫入。
下面我們的代碼測試
首先是我們的TSLog(表示的意思是ThreadSpecificLog,線程特有Log類)類:
package justTest;import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter;public class TSLog {private PrintWriter writer = null;// 初始化writer字段public TSLog(String fileName) {try {writer = new PrintWriter(new FileWriter(fileName));} catch (IOException e) {e.printStackTrace();}}// 加入一條logpublic void println(String s) {writer.println(s);}// 關(guān)閉logpublic void close() {writer.println("====End of log====");writer.close();} } 接下來是我們的Log類,主要作用是獲取當(dāng)前線程的TSLog,然后調(diào)用其方法:
package justTest;public class Log {private static final ThreadLocal<TSLog> tsLogCollection = new ThreadLocal<TSLog>();//加入一條logpublic static void println(String s){getTSLog().println(s);}//關(guān)閉logpublic static void close(){getTSLog().close();}//取得線程特有的logprivate static TSLog getTSLog(){TSLog tsLog = (TSLog)tsLogCollection.get();//如果線程第一次調(diào)用 就建立新文件并且注冊logif(tsLog == null){tsLog = new TSLog(Thread.currentThread().getName()+"-log.txt");tsLogCollection.set(tsLog);}return tsLog;} } 下面是ClientThread類,繼承自Thread類,使用Log.println和Log.close方法。
package justTest;public class ClientThread extends Thread {public ClientThread(String name){super(name);}public void run(){System.out.println(getName()+"BEGIN");for(int i = 0;i<10;i++){Log.println("i="+i);try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}Log.close();System.out.println(getName()+"END");} } 最后是我們的Test類,啟動ClientThread線程:
package justTest;public class Test {public static void main(String[] args) {new ClientThread("Alice").start();new ClientThread("Bobby").start();new ClientThread("Chris").start();} } 現(xiàn)在回頭再來看看我們的代碼:
在TSLog類中,這個類負(fù)責(zé)建立線程特有的log記錄,而Log類負(fù)責(zé)調(diào)用TSLog類下的方法以及獲得線程特有的log,騎寵tsLogCollection字段就是用來儲藏每個線程的TSLog實例的保管箱。其他的沒什么可說的,前面幾篇文章說太多了。
Thread-Specific Storage Pattern的所有參與者
Client(委托人)參與者
Client參與者將工作委托給TSObjectProxy參與者。一個TSObjectProxy參與者可由多個Client參與者一起使用。比如示例中的ClientThread類。
TSObjectProxy(線程獨有對象的代理者)參與者
TSObjectProxy參與者會處理多個Client參與者委托的工作。
首先,TSObjectProxy參與者會使用TSObjectCollection參與者,取得Client參與者所對應(yīng)的TSObject參與者。并且將工作委托給TSObject參與者,比如示例中的Log類。
TSObjectCollection(線程獨有對象的集合)參與者
TSObjectCollection參與者擁有Client參與者與TSObject參與者的對照表。
當(dāng)getTSObject被調(diào)用時,就檢查對照表,返回Client參與者對應(yīng)的TSObject對象。而setTSObject方法被調(diào)用時,則在對照表里設(shè)置Client參與者與TSObject參與者的組合。比如示例中的ThreadLocal類。
TSObject(線程獨有的對象)參與者
TSObject參與者存放有線程特有的信息,這個參與者由TSObjectCollection管理。TSObject參與者的方法只會由單線程調(diào)用。比如示例中的TSLog類。
擴(kuò)展思考
局部變量與java.lang.ThreadLocal類
線程本來就有特有的區(qū)域,就是存放方法局部變量的堆棧。在方法里分配的局部變量都是線程獨有的,無法被其他線程訪問到。但是這些變量一旦推出方法就會消失。而ThreadLocal則是和方法調(diào)用無關(guān),為線程分配特有空間的類。
放置線程特有信息的地方
1、線程外(thread-external)
java.lang.ThreadLocal的示例可以說是保管箱間。每個線程的保管箱都集中在保管箱間里。線程不會帶著保管箱到處跑。像這樣就被稱為線程外的信息存放。這樣的方式不需要修改表示線程的現(xiàn)有類,然而也存在線程類不容易被閱讀的危險,畢竟無法得知其他類中存放有線程的信息。
2、線程內(nèi)(thread-internal)
現(xiàn)在有一個Thread類的子類MyThread,MyThread的字段就是線程特有的信息,這時我們就稱呼位線程內(nèi)存放的信息。
將特有信息存放在線程外就像是自己所有,但是自己不一定帶在身上;
而特有信息存放在線程的方式,就像拿在自己手上一樣。
不必?fù)?dān)心被其他線程訪問
這個模式下線程獨有的內(nèi)存空間是不用擔(dān)心被其他線程擅自亂動的。
這是很重要的性質(zhì),共享互斥是很重要的,但是實現(xiàn)共享互斥并不容易,而且要求共享實例不損壞,共享互斥執(zhí)行性能偏低。然而這個模式保證了線程內(nèi)部的屬性絕對不會被其他線程碰到,而且沒有出現(xiàn)進(jìn)行共享互斥的操作(也許底層有,但是至少不用我們寫),是很方便的架構(gòu)。畢竟這個模式?jīng)]有出現(xiàn)SharedResource。
throughput的提升取決于實現(xiàn)
雖然這個模式需要我們寫的代碼沒有共享互斥,但是程序的throughput不見得一定比Single Threaded Execution Pattern來得高。因為互斥控制可能存在于TSObjectCollection參與者內(nèi),另外通過TSObjectProxy參與者調(diào)用方法也需要花費時間(畢竟要通過TSObject)。
然而這個模式的優(yōu)點不是在于throughput上,而是復(fù)用性:
1、不需要改變程序的結(jié)構(gòu)
2、互斥共享不在表面上,減少了犯錯的機(jī)會
隱藏了context
這應(yīng)該可以說是這個模式的一個缺點了,TSObjectCollection參與者(相當(dāng)于ThreadLocal)會自己判斷現(xiàn)在的線程而不需要程序員傳入,雖然方便,但是也危險。
總結(jié)
以上是生活随笔為你收集整理的Thread-Specific Storage Pattern的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php实现鼠标悬停显示下拉菜单,jque
- 下一篇: Java学习笔记 第一天