ThreadLocal的核心思想很簡(jiǎn)單:為每個(gè)獨(dú)立的線程提供一個(gè)變量的副本。
Java提供的synchronized關(guān)鍵字使用了“同步鎖”的機(jī)制來(lái)阻止線程的競(jìng)爭(zhēng)訪問,即“以時(shí)間換空間”。: " 10pt; FONT-SIZE:> ThreadLocal則使用了“拷貝副本”的方式,人人有份,你用你的,我用我的,大家互不影響,是“以空間換時(shí)間”。每個(gè)線程修改變量時(shí),實(shí)際上修改的是變量的副本,不怕影響到其它線程。
為了加深對(duì)ThreadLocal的理解,下面我使用一個(gè)例子來(lái)演示ThreadLocal如何隔離線程間的變量訪問和修改:
【1】SerialNum類
packageexample.thread.threadLocal;
publicclassSerialNum{
privatestaticintnextSerialNum=1;
@SuppressWarnings("unchecked")
privatestaticThreadLocalserialNum=newThreadLocal(){
protectedsynchronizedObjectinitialValue(){
returnnewInteger(nextSerialNum++);
}
};
publicstaticintget(){
return((Integer)(serialNum.get())).intValue();
}
@SuppressWarnings("unchecked")
publicstaticvoidset(IntegernewSerial){
serialNum.set(newSerial);
}
}
【2】GetSerialNumThread
packageexample.thread.threadLocal;
publicclassGetSerialNumThreadimplementsRunnable{
publicstaticvoidmain(Stringargs[]){
GetSerialNumThreadserialNumGetter=newGetSerialNumThread();
Threadt1=newThread(serialNumGetter,"ThreadA");
Threadt2=newThread(serialNumGetter,"ThreadB");
t1.start();
try{
t1.join();
}catch(InterruptedExceptione){
e.printStackTrace();
} t2.start();
}
publicvoidrun(){
intmySerialNum=getSerialNum();
System.out.println("線程"+Thread.currentThread().getName()
+"獲取到的序列號(hào)是"+mySerialNum);
System.out.println("線程"+Thread.currentThread().getName()
+"修改了序列號(hào)為"+(mySerialNum*3));
setSerialNum(mySerialNum*3);
System.out.println("線程"+Thread.currentThread().getName()
+"再次獲得的序列號(hào)是"+getSerialNum());
}
privateintgetSerialNum(){
returnSerialNum.get();
}
privatevoidsetSerialNum(intnewSerialNum){
SerialNum.set(newInteger(newSerialNum));
}
} 運(yùn)行的結(jié)果如下:
線程 Thread A 獲取到的序列號(hào)是1
線程 Thread A 修改了序列號(hào)為3
線程 Thread A 再次獲得的序列號(hào)是3
線程 Thread B 獲取到的序列號(hào)是2
線程 Thread B 修改了序列號(hào)為6
線程 Thread B 再次獲得的序列號(hào)是6
可見第一個(gè)線程在調(diào)用SerialNum.set(int)方法修改static變量時(shí),其實(shí)修改的是它自己的副本,而不是修改本地變量,第二個(gè)線程在初始化的時(shí)候拿到的序列號(hào)是2而不是7。
為什么會(huì)這樣呢?明明serialNum是靜態(tài)變量???其實(shí)我們只需要看看ThreadLocal的內(nèi)部構(gòu)造就知道了:
A. ThreadLocal的get()方法:
/**
*Returnsthevalueinthecurrentthread’scopyofthisthread-local
*variable. Createsandinitializesthecopyifthisisthefirsttime
*thethreadhascalledthismethod.
*
*@returnthecurrentthread’svalueofthisthread-local
*/
publicTget(){
Threadt=Thread.currentThread();
ThreadLocalMapmap=getMap(t);
if(map!=null)
return(T)map.get(this);
//Mapsareconstructedlazily. ifthemapforthisthread
//doesn’texist,createit,withthisThreadLocalandits
//initialvalueasitsonlyentry.
Tvalue=initialValue();
createMap(t,value);
returnvalue;
}
B. ThreadLocal的set()方法:
/**
*Setsthecurrentthread’scopyofthisthread-localvariable
*tothespecifiedvalue. Manyapplicationswillhavenoneedfor
*thisfunctionality,relyingsolelyonthe{@link#initialValue}
*methodtosetthevaluesofthread-locals.
*
*@paramvaluethevaluetobestoredinthecurrentthreads’copyof
* thisthread-local.
*/
publicvoidset(Tvalue){
Threadt=Thread.currentThread();
ThreadLocalMapmap=getMap(t);
if(map!=null)
map.set(this,value);
else
createMap(t,value);
}
可以看到ThreadLocal在內(nèi)部維護(hù)了一個(gè)Map,將變量的值和線程綁定起來(lái),get/set方法都是對(duì)該線程對(duì)應(yīng)的value進(jìn)行操作,所以不會(huì)影響到其它線程。
Java提供的synchronized關(guān)鍵字使用了“同步鎖”的機(jī)制來(lái)阻止線程的競(jìng)爭(zhēng)訪問,即“以時(shí)間換空間”。: " 10pt; FONT-SIZE:> ThreadLocal則使用了“拷貝副本”的方式,人人有份,你用你的,我用我的,大家互不影響,是“以空間換時(shí)間”。每個(gè)線程修改變量時(shí),實(shí)際上修改的是變量的副本,不怕影響到其它線程。
為了加深對(duì)ThreadLocal的理解,下面我使用一個(gè)例子來(lái)演示ThreadLocal如何隔離線程間的變量訪問和修改:
【1】SerialNum類
packageexample.thread.threadLocal;
publicclassSerialNum{
privatestaticintnextSerialNum=1;
@SuppressWarnings("unchecked")
privatestaticThreadLocalserialNum=newThreadLocal(){
protectedsynchronizedObjectinitialValue(){
returnnewInteger(nextSerialNum++);
}
};
publicstaticintget(){
return((Integer)(serialNum.get())).intValue();
}
@SuppressWarnings("unchecked")
publicstaticvoidset(IntegernewSerial){
serialNum.set(newSerial);
}
}
【2】GetSerialNumThread
packageexample.thread.threadLocal;
publicclassGetSerialNumThreadimplementsRunnable{
publicstaticvoidmain(Stringargs[]){
GetSerialNumThreadserialNumGetter=newGetSerialNumThread();
Threadt1=newThread(serialNumGetter,"ThreadA");
Threadt2=newThread(serialNumGetter,"ThreadB");
t1.start();
try{
t1.join();
}catch(InterruptedExceptione){
e.printStackTrace();
} t2.start();
}
publicvoidrun(){
intmySerialNum=getSerialNum();
System.out.println("線程"+Thread.currentThread().getName()
+"獲取到的序列號(hào)是"+mySerialNum);
System.out.println("線程"+Thread.currentThread().getName()
+"修改了序列號(hào)為"+(mySerialNum*3));
setSerialNum(mySerialNum*3);
System.out.println("線程"+Thread.currentThread().getName()
+"再次獲得的序列號(hào)是"+getSerialNum());
}
privateintgetSerialNum(){
returnSerialNum.get();
}
privatevoidsetSerialNum(intnewSerialNum){
SerialNum.set(newInteger(newSerialNum));
}
} 運(yùn)行的結(jié)果如下:
線程 Thread A 獲取到的序列號(hào)是1
線程 Thread A 修改了序列號(hào)為3
線程 Thread A 再次獲得的序列號(hào)是3
線程 Thread B 獲取到的序列號(hào)是2
線程 Thread B 修改了序列號(hào)為6
線程 Thread B 再次獲得的序列號(hào)是6
可見第一個(gè)線程在調(diào)用SerialNum.set(int)方法修改static變量時(shí),其實(shí)修改的是它自己的副本,而不是修改本地變量,第二個(gè)線程在初始化的時(shí)候拿到的序列號(hào)是2而不是7。
為什么會(huì)這樣呢?明明serialNum是靜態(tài)變量???其實(shí)我們只需要看看ThreadLocal的內(nèi)部構(gòu)造就知道了:
A. ThreadLocal的get()方法:
/**
*Returnsthevalueinthecurrentthread’scopyofthisthread-local
*variable. Createsandinitializesthecopyifthisisthefirsttime
*thethreadhascalledthismethod.
*
*@returnthecurrentthread’svalueofthisthread-local
*/
publicTget(){
Threadt=Thread.currentThread();
ThreadLocalMapmap=getMap(t);
if(map!=null)
return(T)map.get(this);
//Mapsareconstructedlazily. ifthemapforthisthread
//doesn’texist,createit,withthisThreadLocalandits
//initialvalueasitsonlyentry.
Tvalue=initialValue();
createMap(t,value);
returnvalue;
}
B. ThreadLocal的set()方法:
/**
*Setsthecurrentthread’scopyofthisthread-localvariable
*tothespecifiedvalue. Manyapplicationswillhavenoneedfor
*thisfunctionality,relyingsolelyonthe{@link#initialValue}
*methodtosetthevaluesofthread-locals.
*
*@paramvaluethevaluetobestoredinthecurrentthreads’copyof
* thisthread-local.
*/
publicvoidset(Tvalue){
Threadt=Thread.currentThread();
ThreadLocalMapmap=getMap(t);
if(map!=null)
map.set(this,value);
else
createMap(t,value);
}
可以看到ThreadLocal在內(nèi)部維護(hù)了一個(gè)Map,將變量的值和線程綁定起來(lái),get/set方法都是對(duì)該線程對(duì)應(yīng)的value進(jìn)行操作,所以不會(huì)影響到其它線程。