線程狀態(tài)圖
線程共包括以下5種狀態(tài)。
1. 新建狀態(tài)(New) : 線程對象被創(chuàng)建后,就進入了新建狀態(tài)。例如,Thread thread = new Thread()。
2. 就緒狀態(tài)(Runnable): 也被稱為“可執(zhí)行狀態(tài)”。線程對象被創(chuàng)建后,其它線程調(diào)用了該對象的start()方法,從而來啟動該線程。例如,thread.start()。處于就緒狀態(tài)的線程,隨時可能被CPU調(diào)度執(zhí)行。
3. 運行狀態(tài)(Running) : 線程獲取CPU權(quán)限進行執(zhí)行。需要注意的是,線程只能從就緒狀態(tài)進入到運行狀態(tài)。
4. 阻塞狀態(tài)(Blocked) : 阻塞狀態(tài)是線程因為某種原因放棄CPU使用權(quán),暫時停止運行。直到線程進入就緒狀態(tài),才有機會轉(zhuǎn)到運行狀態(tài)。阻塞的情況分三種:
(01) 等待阻塞 -- 通過調(diào)用線程的wait()方法,讓線程等待某工作的完成。
(02) 同步阻塞 -- 線程在獲取synchronized同步鎖失敗(因為鎖被其它線程所占用),它會進入同步阻塞狀態(tài)。
(03) 其他阻塞 -- 通過調(diào)用線程的sleep()或join()或發(fā)出了I/O請求時,線程會進入到阻塞狀態(tài)。當sleep()狀態(tài)超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉(zhuǎn)入就緒狀態(tài)。
5. 死亡狀態(tài)(Dead) : 線程執(zhí)行完了或者因異常退出了run()方法,該線程結(jié)束生命周期。
實現(xiàn)多線程的方式Thread和Runnable
Thread:繼承thread類,實現(xiàn)run方法,在main函數(shù)中調(diào)用start方法啟動線程
Runnable:接口,實現(xiàn)Runnable接口,作為參數(shù)傳遞給Thread的構(gòu)造函數(shù),在main中調(diào)用start方法
例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
class task implements Runnable { private int ticket = 10 ; @Override public void run() { for ( int i = 0 ; i < 20 ; i++) { if ( this .ticket > 0 ) { System.out.println(Thread.currentThread().getName() + " sold ticket " + this .ticket--); } } } }; public class RunnableTest { public static void main(String[]args){ task mytask = new task(); Thread t1 = new Thread(mytask); Thread t2 = new Thread(mytask); Thread t3 = new Thread(mytask); t1.start(); t2.start(); t3.start(); } } //ThreadTest.java 源碼 class MyThread extends Thread { private int ticket = 10 ; public void run() { for ( int i = 0 ; i < 20 ; i++) { if ( this .ticket > 0 ) { System.out.println( this .getName() + " 賣票:ticket" + this .ticket--); } } } } public class ThreadTest { public static void main(String[] args) { // 啟動3個線程t1,t2,t3;每個線程各賣10張票! MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); MyThread t3 = new MyThread(); t1.start(); t2.start(); t3.start(); } }; |
Thread與Runnable的區(qū)別
Thread 是類,而Runnable是接口;Thread本身是實現(xiàn)了Runnable接口的類。我們知道“一個類只能有一個父類,但是卻能實現(xiàn)多個接口”,因此Runnable具有更好的擴展性。此外,Runnable還可以用于“資源的共享”。即,多個線程都是基于某一個Runnable對象建立的,它們會共享Runnable對象上的資源。通常,建議通過“Runnable”實現(xiàn)多線程!
Thread的run與start
start() : 它的作用是啟動一個新線程,新線程會執(zhí)行相應(yīng)的run()方法。start()不能被重復(fù)調(diào)用。start()實際上是通過本地方法start0()啟動線程的。而start0()會新運行一個線程,新線程會調(diào)用run()方法。
run() : run()就和普通的成員方法一樣,可以被重復(fù)調(diào)用。單獨調(diào)用run()的話,會在當前線程中執(zhí)行run(),而并不會啟動新線程!run()就是直接調(diào)用Thread線程的Runnable成員的run()方法,并不會新建一個線程。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
// Demo.java 的源碼 class MyThread extends Thread{ public MyThread(String name) { super (name); } public void run(){ System.out.println(Thread.currentThread().getName()+ " is running" ); } }; public class Demo { public static void main(String[] args) { Thread mythread= new MyThread( "mythread" ); System.out.println(Thread.currentThread().getName()+ " call mythread.run()" ); mythread.run(); System.out.println(Thread.currentThread().getName()+ " call mythread.start()" ); mythread.start(); } } |
輸出:
1
2
3
4
|
main call mythread.run() main is running main call mythread.start() mythread is running |
synchronized
在java中每個對象都有一個同步鎖,當我們調(diào)用對象的synchronized方法就獲取了對象鎖,synchronized(obj)就獲取了“obj這個對象”的同步鎖.不同線程對同步鎖的訪問是互斥的.某時間點對象的同步鎖只能被一個線程獲取到.通過同步鎖,我們就能在多線程中,實現(xiàn)對“對象/方法”的互斥訪問. 例如,現(xiàn)在有兩個線程A和線程B,它們都會訪問“對象obj的同步鎖”。假設(shè),在某一時刻,線程A獲取到“obj的同步鎖”并在執(zhí)行一些操作;而此時,線程B也企圖獲取“obj的同步鎖” —— 線程B會獲取失敗,它必須等待,直到線程A釋放了“該對象的同步鎖”之后線程B才能獲取到“obj的同步鎖”從而才可以運行。
基本規(guī)則
第一條: 當一個線程訪問“某對象”的“synchronized方法”或者“synchronized代碼塊”時,其他線程對“該對象”的該“synchronized方法”或者“synchronized代碼塊”的訪問將被阻塞。
第二條: 當一個線程訪問“某對象”的“synchronized方法”或者“synchronized代碼塊”時,其他線程仍然可以訪問“該對象”的非同步代碼塊。
第三條: 當一個線程訪問“某對象”的“synchronized方法”或者“synchronized代碼塊”時,其他線程對“該對象”的其他的“synchronized方法”或者“synchronized代碼塊”的訪問將被阻塞。
synchronized 方法
1
2
3
4
5
6
7
8
9
10
|
public synchronized void foo1() { System.out.println( "synchronized methoed" ); } synchronized 代碼塊 public void foo2() { synchronized ( this ) { System.out.println( "synchronized methoed" ); } } |
synchronized代碼塊中的this是指當前對象。也可以將this替換成其他對象,例如將this替換成obj,則foo2()在執(zhí)行synchronized(obj)時就獲取的是obj的同步鎖。
synchronized代碼塊可以更精確的控制沖突限制訪問區(qū)域,有時候表現(xiàn)更高效率
實例鎖 和 全局鎖
實例鎖 -- 鎖在某一個實例對象上。如果該類是單例,那么該鎖也具有全局鎖的概念。實例鎖對應(yīng)的就是synchronized關(guān)鍵字。
全局鎖 -- 該鎖針對的是類,無論實例多少個對象,那么線程都共享該鎖。全局鎖對應(yīng)的就是static synchronized(或者是鎖在該類的class或者classloader對象上)。
1
2
3
4
5
6
|
pulbic class Something { public synchronized void isSyncA(){} public synchronized void isSyncB(){} public static synchronized void cSyncA(){} public static synchronized void cSyncB(){} } |
(01) x.isSyncA()與x.isSyncB() 不能被同時訪問。因為isSyncA()和isSyncB()都是訪問同一個對象(對象x)的同步鎖!
(02) x.isSyncA()與y.isSyncA() 可以同時被訪問。因為訪問的不是同一個對象的同步鎖,x.isSyncA()訪問的是x的同步鎖,而y.isSyncA()訪問的是y的同步鎖。
(03) x.cSyncA()與y.cSyncB()不能被同時訪問。因為cSyncA()和cSyncB()都是static類型,x.cSyncA()相當于Something.isSyncA(),y.cSyncB()相當于Something.isSyncB(),因此它們共用一個同步鎖,不能被同時反問。
(04) x.isSyncA()與Something.cSyncA() 可以被同時訪問。因為isSyncA()是實例方法,x.isSyncA()使用的是對象x的鎖;而cSyncA()是靜態(tài)方法,Something.cSyncA()可以理解對使用的是“類的鎖”。因此,它們是可以被同時訪問的。
線程阻塞與喚醒wait,notify,notifyAll
在Object.java中,定義了wait(), notify()和notifyAll()等接口。wait()的作用是讓當前線程進入等待狀態(tài),同時,wait()也會讓當前線程釋放它所持有的鎖。而notify()和notifyAll()的作用,則是喚醒當前對象上的等待線程;notify()是喚醒單個線程,而notifyAll()是喚醒所有的線程。
Object類中關(guān)于等待/喚醒的API詳細信息如下:
notify() -- 喚醒在此對象監(jiān)視器上等待的單個線程。
notifyAll() -- 喚醒在此對象監(jiān)視器上等待的所有線程。
wait() -- 讓當前線程處于“等待(阻塞)狀態(tài)”,“直到其他線程調(diào)用此對象的 notify() 方法或 notifyAll() 方法”,當前線程被喚醒(進入“就緒狀態(tài)”)。
wait(long timeout) -- 讓當前線程處于“等待(阻塞)狀態(tài)”,“直到其他線程調(diào)用此對象的 notify() 方法或 notifyAll() 方法,或者超過指定的時間量”,當前線程被喚醒(進入“就緒狀態(tài)”)。
wait(long timeout, int nanos) -- 讓當前線程處于“等待(阻塞)狀態(tài)”,“直到其他線程調(diào)用此對象的 notify() 方法或 notifyAll() 方法,或者其他某個線程中斷當前線程,或者已超過某個實際時間量”,當前線程被喚醒(進入“就緒狀態(tài)”)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
// WaitTest.java的源碼 class ThreadA extends Thread{ public ThreadA(String name) { super (name); } public void run() { synchronized ( this ) { System.out.println(Thread.currentThread().getName()+ " call notify()" ); // 喚醒當前的wait線程 notify(); } } } public class WaitTest { public static void main(String[] args) { ThreadA t1 = new ThreadA( "t1" ); synchronized (t1) { try { // 啟動“線程t1” System.out.println(Thread.currentThread().getName()+ " start t1" ); t1.start(); // 主線程等待t1通過notify()喚醒。 System.out.println(Thread.currentThread().getName()+ " wait()" ); t1.wait(); System.out.println(Thread.currentThread().getName()+ " continue" ); } catch (InterruptedException e) { e.printStackTrace(); } } } } |
輸出
1
2
3
4
|
main start t1 main wait() t1 call notify() main continue |
(01) 注意,圖中"主線程" 代表“主線程main”。"線程t1" 代表WaitTest中啟動的“線程t1”。 而“鎖” 代表“t1這個對象的同步鎖”。
(02) “主線程”通過 new ThreadA("t1") 新建“線程t1”。隨后通過synchronized(t1)獲取“t1對象的同步鎖”。然后調(diào)用t1.start()啟動“線程t1”。
(03) “主線程”執(zhí)行t1.wait() 釋放“t1對象的鎖”并且進入“等待(阻塞)狀態(tài)”。等待t1對象上的線程通過notify() 或 notifyAll()將其喚醒。
(04) “線程t1”運行之后,通過synchronized(this)獲取“當前對象的鎖”;接著調(diào)用notify()喚醒“當前對象上的等待線程”,也就是喚醒“主線程”。
(05) “線程t1”運行完畢之后,釋放“當前對象的鎖”。緊接著,“主線程”獲取“t1對象的鎖”,然后接著運行。
t1.wait()是通過“線程t1”調(diào)用的wait()方法,但是調(diào)用t1.wait()的地方是在“主線程main”中。而主線程必須是“當前線程”,也就是運行狀態(tài),才可以執(zhí)行t1.wait()。所以,此時的“當前線程”是“主線程main”!因此,t1.wait()是讓“主線程”等待,而不是“線程t1”!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
package thread.Test; public class NotifyAllTest { private static Object obj = new Object(); public static void main(String[] args) { ThreadA t1 = new ThreadA( "t1" ); ThreadA t2 = new ThreadA( "t2" ); ThreadA t3 = new ThreadA( "t3" ); t1.start(); t2.start(); t3.start(); try { System.out.println(Thread.currentThread().getName()+ " sleep(3000)" ); Thread.sleep( 3000 ); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (obj) { System.out.println(Thread.currentThread().getName()+ " notifyAll()" ); obj.notifyAll(); //在此喚醒t1.t2.t3 } } static class ThreadA extends Thread{ public ThreadA(String name){ super (name); } public void run() { synchronized (obj) { try { // 打印輸出結(jié)果 System.out.println(Thread.currentThread().getName() + " wait" ); //釋放obj對象鎖 obj.wait(); // 打印輸出結(jié)果 System.out.println(Thread.currentThread().getName() + " continue" ); } catch (InterruptedException e) { e.printStackTrace(); } } } } } |
輸出:
1
2
3
4
5
6
7
8
|
t1 wait main sleep(3000) t3 wait t2 wait main notifyAll() t2 continue t3 continue t1 continue |
(01) 主線程中新建并且啟動了3個線程"t1", "t2"和"t3"。
(02) 主線程通過sleep(3000)休眠3秒。在主線程休眠3秒的過程中,我們假設(shè)"t1", "t2"和"t3"這3個線程都運行了。以"t1"為例,當它運行的時候,它會執(zhí)行obj.wait()等待其它線程通過notify()或額nofityAll()來喚醒它;相同的道理,"t2"和"t3"也會等待其它線程通過nofity()或nofityAll()來喚醒它們。
(03) 主線程休眠3秒之后,接著運行。執(zhí)行 obj.notifyAll() 喚醒obj上的等待線程,即喚醒"t1", "t2"和"t3"這3個線程。 緊接著,主線程的synchronized(obj)運行完畢之后,主線程釋放“obj鎖”。這樣,"t1", "t2"和"t3"就可以獲取“obj鎖”而繼續(xù)運行了!
notify,notifyall與鎖的關(guān)系
Object中的wait(), notify()等函數(shù),和synchronized一樣,會對“對象的同步鎖”進行操作。
wait()會使“當前線程”等待,因為線程進入等待狀態(tài),所以線程應(yīng)該釋放它鎖持有的“同步鎖”,否則其它線程獲取不到該“同步鎖”而無法運行!
OK,線程調(diào)用wait()之后,會釋放它鎖持有的“同步鎖”;而且,根據(jù)前面的介紹,我們知道:等待線程可以被notify()或notifyAll()喚醒。現(xiàn)在,請思考一個問題:notify()是依據(jù)什么喚醒等待線程的?或者說,wait()等待線程和notify()之間是通過什么關(guān)聯(lián)起來的?答案是:依據(jù)“對象的同步鎖”。
負責(zé)喚醒等待線程的那個線程(我們稱為“喚醒線程”),它只有在獲取“該對象的同步鎖”(這里的同步鎖必須和等待線程的同步鎖是同一個),并且調(diào)用notify()或notifyAll()方法之后,才能喚醒等待線程。雖然,等待線程被喚醒;但是,它不能立刻執(zhí)行,因為喚醒線程還持有“該對象的同步鎖”。必須等到喚醒線程釋放了“對象的同步鎖”之后,等待線程才能獲取到“對象的同步鎖”進而繼續(xù)運行。
總之,notify(), wait()依賴于“同步鎖”,而“同步鎖”是對象鎖持有,并且每個對象有且僅有一個!這就是為什么notify(), wait()等函數(shù)定義在Object類,而不是Thread類中的原因。
線程讓步y(tǒng)ield
線程讓步,使線程從執(zhí)行狀態(tài)變?yōu)榫途w狀態(tài),從而讓其它具有相同優(yōu)先級的等待線程獲取執(zhí)行權(quán);但是,并不能保證在當前線程調(diào)用yield()之后,其它具有相同優(yōu)先級的線程就一定能獲得執(zhí)行權(quán);也有可能是當前線程又進入到“運行狀態(tài)”繼續(xù)運行。
yield 與wait
(01) wait()是讓線程由“運行狀態(tài)”進入到“等待(阻塞)狀態(tài)”,而不yield()是讓線程由“運行狀態(tài)”進入到“就緒狀態(tài)”。
(02) wait()是會線程釋放它所持有對象的同步鎖,而yield()方法不會釋放鎖。
(03) wait是object的方法,yield是Thread的方法
線程休眠 sleep
sleep() 的作用是讓當前線程休眠,即當前線程會從“運行狀態(tài)”進入到“休眠(阻塞)狀態(tài)”。sleep()會指定休眠時間,線程休眠的時間會大于/等于該休眠時間;在線程重新被喚醒時,它會由“阻塞狀態(tài)”變成“就緒狀態(tài)”,從而等待cpu的調(diào)度執(zhí)行。
sleep與wait的區(qū)別
wait()的作用是讓當前線程由“運行狀態(tài)”進入“等待(阻塞)狀態(tài)”的同時,也會釋放同步鎖。而sleep()的作用是也是讓當前線程由“運行狀態(tài)”進入到“休眠(阻塞)狀態(tài)。(這個其實區(qū)別不大)
wait()會釋放對象的同步鎖,而sleep()則不會釋放鎖
wait是object的方法,sleep是Thread的方法
join
讓主線程等待,子線程運行完畢,主線程才能繼續(xù)運行
interrupt
用來終止處于阻塞狀態(tài)的線程
1
2
3
4
5
6
7
8
9
10
|
@Override public void run() { try { while ( true ) { // 執(zhí)行任務(wù)... } } catch (InterruptedException ie) { // 由于產(chǎn)生InterruptedException異常,退出while(true)循環(huán),線程終止! } } |
在while(true)中不斷的執(zhí)行任務(wù),當線程處于阻塞狀態(tài)時,調(diào)用線程的interrupt()產(chǎn)生InterruptedException中斷。中斷的捕獲在while(true)之外,這樣就退出了while(true)循環(huán)
終止處于運行狀態(tài)的線程
1
2
3
4
5
6
|
@Override public void run() { while (!isInterrupted()) { // 執(zhí)行任務(wù)... } } |
通用的終止線程的方式
1
2
3
4
5
6
7
8
9
10
11
|
@Override public void run() { try { // 1. isInterrupted()保證,只要中斷標記為true就終止線程。 while (!isInterrupted()) { // 執(zhí)行任務(wù)... } } catch (InterruptedException ie) { // 2. InterruptedException異常保證,當InterruptedException異常產(chǎn)生時,線程被終止。 } } |
線程優(yōu)先級
java 中的線程優(yōu)先級的范圍是1~10,默認的優(yōu)先級是5。“高優(yōu)先級線程”會優(yōu)先于“低優(yōu)先級線程”執(zhí)行。java 中有兩種線程:用戶線程和守護線程。可以通過isDaemon()方法來區(qū)別它們:如果返回false,則說明該線程是“用戶線程”;否則就是“守護線程”。用戶線程一般用戶執(zhí)行用戶級任務(wù),而守護線程也就是“后臺線程”,一般用來執(zhí)行后臺任務(wù)。需要注意的是:Java虛擬機在“用戶線程”都結(jié)束后會后退出。
每個線程都有一個優(yōu)先級。“高優(yōu)先級線程”會優(yōu)先于“低優(yōu)先級線程”執(zhí)行。每個線程都可以被標記為一個守護進程或非守護進程。在一些運行的主線程中創(chuàng)建新的子線程時,子線程的優(yōu)先級被設(shè)置為等于“創(chuàng)建它的主線程的優(yōu)先級”,當且僅當“創(chuàng)建它的主線程是守護線程”時“子線程才會是守護線程”。
當Java虛擬機啟動時,通常有一個單一的非守護線程(該線程通過是通過main()方法啟動)。JVM會一直運行直到下面的任意一個條件發(fā)生,JVM就會終止運行:
(01) 調(diào)用了exit()方法,并且exit()有權(quán)限被正常執(zhí)行。
(02) 所有的“非守護線程”都死了(即JVM中僅僅只有“守護線程”)。
守護進程
(01) 主線程main是用戶線程,它創(chuàng)建的子線程t1也是用戶線程。
(02) t2是守護線程。在“主線程main”和“子線程t1”(它們都是用戶線程)執(zhí)行完畢,只剩t2這個守護線程的時候,JVM自動退出。
生產(chǎn)者消費者問題
(01) 生產(chǎn)者僅僅在倉儲未滿時候生產(chǎn),倉滿則停止生產(chǎn)。
(02) 消費者僅僅在倉儲有產(chǎn)品時候才能消費,倉空則等待。
(03) 當消費者發(fā)現(xiàn)倉儲沒產(chǎn)品可消費時候會通知生產(chǎn)者生產(chǎn)。
(04) 生產(chǎn)者在生產(chǎn)出可消費產(chǎn)品時候,應(yīng)該通知等待的消費者去消費。
線程之間的通信
方式:共享內(nèi)存和消息傳遞
共享內(nèi)存:線程A和線程B共享內(nèi)存,線程A更新共享變量的值,刷新到主內(nèi)存中,線程B去主內(nèi)存中讀取線程A更新后的變量。整個通信過程必須通過主內(nèi)存。同步是顯式進行的。
如果一個變量是volatile類型,則對該變量的讀寫就將具有原子性。如果是多個volatile操作或類似于volatile++這種復(fù)合操作,這些操作整體上不具有原子性。
volatile變量自身具有下列特性:
[可見性]:對一個volatile變量的讀,總是能看到(任意線程)對這個volatile變量最后的寫入。
[原子性]:對任意單個volatile變量的讀/寫具有原子性,但類似于volatile++這種復(fù)合操作不具有原子性。
volatile寫:當寫一個volatile變量時,JMM會把該線程對應(yīng)的本地內(nèi)存中的共享變量刷新到主內(nèi)存。
volatile讀:當讀一個volatile變量時,JMM會把該線程對應(yīng)的本地內(nèi)存置為無效。線程接下來將從主內(nèi)存中讀取共享變量。
消息傳遞:消息的發(fā)送在消息的接受之前,同步隱式進行。
ThreadLocal
ThreadLocal 不是用來解決共享對象的多線程訪問問題的,一般情況下,通過ThreadLocal.set() 到線程中的對象是該線程自己使用的對象,其他線程是不需要訪問的,也訪問不到的.ThreadLocal使得各線程能夠保持各自獨立的一個對象,并不是通過ThreadLocal.set()來實現(xiàn)的,而是通過每個線程中的new 對象 的操作來創(chuàng)建的對象,每個線程創(chuàng)建一個,不是什么對象的拷貝或副本。通過ThreadLocal.set()將這個新創(chuàng)建的對象的引用保存到各線程的自己的一個map中,每個線程都有這樣一個map,執(zhí)行ThreadLocal.get()時,各線程從自己的map中取出放進去的對象,因此取出來的是各自自己線程中的對象,ThreadLocal實例是作為map的key來使用的。 如果ThreadLocal.set()進去的東西本來就是多個線程共享的同一個對象,那么多個線程的ThreadLocal.get()取得的還是這個共享對象本身,還是有并發(fā)訪問問題。
ThreadLocal的一個實現(xiàn)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * 使用了ThreadLocal的類 * * @author leizhimin 2010-1-5 10:35:27 */ public class MyThreadLocal { //定義了一個ThreadLocal變量,用來保存int或Integer數(shù)據(jù) private com.lavasoft.test2.ThreadLocal<Integer> tl = new com.lavasoft.test2.ThreadLocal<Integer>() { @Override protected Integer initialValue() { return 0 ; } }; public Integer getNextNum() { //將tl的值獲取后加1,并更新設(shè)置t1的值 tl.set(tl.get() + 1 ); return tl.get(); } } class ThreadLocal<T> { private Map<Thread, T> map = Collections.synchronizedMap( new HashMap<Thread, T>()); public ThreadLocal() { } protected T initialValue() { return null ; } public T get() { Thread t = Thread.currentThread(); T obj = map.get(t); if (obj == null && !map.containsKey(t)) { obj = initialValue(); map.put(t, obj); } return obj; } public void set(T value) { map.put(Thread.currentThread(), value); } public void remove() { map.remove(Thread.currentThread()); } } |
事實上ThreadLocal是這樣做的:
1
2
3
4
5
6
7
8
9
10
|
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null ) { ThreadLocalMap.Entry e = map.getEntry( this ); if (e != null ) return (T)e.value; } return setInitialValue(); } |