X-Spirit
Always Beyond the Time
BlogJava
|
首頁
|
發新隨筆
|
發新文章
|
聯系
|
聚合
|
管理
隨筆:91 文章:1 評論:65 引用:0
Java 多線程同步問題的探究(五、你有我有全都有—— ThreadLocal如何解決并發安全性?)【更新重要補疑】
前面我們介紹了Java當中多個線程搶占一個共享資源的問題。但不論是同步還是重入鎖,都不能實實在在的解決資源緊缺的情況,這些方案只是靠制定規則來約束線程的行為,讓它們不再拼命的爭搶,而不是真正從實質上解決他們對資源的需求。
在JDK 1.2當中,引入了java.lang.ThreadLocal。它為我們提供了一種全新的思路來解決線程并發的問題。但是他的名字難免讓我們望文生義:本地線程?
什么是本地線程?
本地線程開玩笑的說:不要迷戀哥,哥只是個傳說。
其實ThreadLocal并非Thread at Local,而是LocalVariable in a Thread。
根據WikiPedia上的介紹,ThreadLocal其實是源于一項多線程技術,叫做Thread Local Storage,即線程本地存儲技術。不僅僅是Java,在C++、C#、.NET、Python、Ruby、Perl等開發平臺上,該技術都已經得以實現。
當使用ThreadLocal維護變量時,它會為每個使用該變量的線程提供獨立的變量副本。也就是說,他從根本上解決的是資源數量的問題,從而使得每個線程持有相對獨立的資源。這樣,當多個線程進行工作的時候,它們不需要糾結于同步的問題,于是性能便大大提升。但資源的擴張帶來的是更多的空間消耗,ThreadLocal就是這樣一種利用空間來換取時間的解決方案。
說了這么多,來看看如何正確使用ThreadLocal。
通過研究JDK文檔,我們知道,ThreadLocal中有幾個重要的方法:get()、set()、remove()、initailValue(),對應的含義分別是:
返回此線程局部變量的當前線程副本中的值、將此線程局部變量的當前線程副本中的值設置為指定值、移除此線程局部變量當前線程的值、返回此線程局部變量的當前線程的“初始值”。
轉載注明出處:http://x- spirit.javaeye.com/、http: //www.tkk7.com/zhangwei217245/
還記得我們在第三篇的上半節引出的那個例子么?幾個線程修改同一個Student對象中的age屬性。為了保證這幾個線程能夠工作正常,我們需要對Student的對象進行同步。
下面我們對這個程序進行一點小小的改造,我們通過繼承Thread來實現多線程:
/**
*
*
@author
x-spirit
*/
public
class
ThreadDemo3
extends
Thread{
private
ThreadLocal
<
Student
>
stuLocal
=
new
ThreadLocal
<
Student
>
();
public
ThreadDemo3(Student stu){
stuLocal.set(stu);
}
public
static
void
main(String[] args) {
Student stu
=
new
Student();
ThreadDemo3 td31
=
new
ThreadDemo3(stu);
ThreadDemo3 td32
=
new
ThreadDemo3(stu);
ThreadDemo3 td33
=
new
ThreadDemo3(stu);
td31.start();
td32.start();
td33.start();
}
@Override
public
void
run() {
accessStudent();
}
public
void
accessStudent() {
String currentThreadName
=
Thread.currentThread().getName();
System.out.println(currentThreadName
+
"
is running!
"
);
Random random
=
new
Random();
int
age
=
random.nextInt(
100
);
System.out.println(
"
thread
"
+
currentThreadName
+
"
set age to:
"
+
age);
Student student
=
stuLocal.get();
student.setAge(age);
System.out.println(
"
thread
"
+
currentThreadName
+
"
first read age is:
"
+
student.getAge());
try
{
Thread.sleep(
5000
);
}
catch
(InterruptedException ex) {
ex.printStackTrace();
}
System.out.println(
"
thread
"
+
currentThreadName
+
"
second read age is:
"
+
student.getAge());
}
}
轉載注明出處:http://x- spirit.javaeye.com/、http: //www.tkk7.com/zhangwei217245/
貌似這個程序沒什么問題。但是運行結果卻顯示:這個程序中的3個線程會拋出3個空指針異常。讀者一定感到很困惑。我明明在構造器當中把Student對象set進了ThreadLocal里面阿,為什么run起來之后居然在調用stuLocal.get()方法的時候得到的是NULL呢?
轉載注明出處:http://x- spirit.javaeye.com/、http: //www.tkk7.com/zhangwei217245/
帶著這個疑問,讓我們深入到JDK的代碼當中,去一看究竟。
轉載注明出處:http://x- spirit.javaeye.com/、http: //www.tkk7.com/zhangwei217245/
原來,在ThreadLocal中,有一個內部類叫做ThreadLocalMap。這個ThreadLocalMap并非java.util.Map的一個實現,而是利用java.lang.ref.WeakReference實現的一個鍵-值對應的數據結構其中,key是ThreadLocal類型,而value是Object類型,我們可以簡單的視為HashMap<ThreadLocal,Object>。
而在每一個Thread對象中,都有一個ThreadLocalMap的引用,即Thread.threadLocals。而ThreadLocal的set方法就是首先嘗試從當前線程中取得ThreadLocalMap(以下簡稱Map)對象。如果取到的不為null,則以ThreadLocal對象自身為key,來取Map中的value。如果取不到Map對象,則首先為當前線程創建一個ThreadLocalMap,然后以ThreadLocal對象自身為key,將傳入的value放入該Map中。
ThreadLocalMap getMap(Thread t) {
return
t.threadLocals;
}
public
void
set(T value) {
Thread t
=
Thread.currentThread();
ThreadLocalMap map
=
getMap(t);
if
(map
!=
null
)
map.set(
this
, value);
else
createMap(t, value);
}
而get方法則是首先得到當前線程的ThreadLocalMap對象,然后,根據ThreadLocal對象自身,取出相應的value。當然,如果在當前線程中取不到ThreadLocalMap對象,則嘗試為當前線程創建ThreadLocalMap對象,并以ThreadLocal對象自身為key,把initialValue()方法產生的對象作為value放入新創建的ThreadLocalMap中。
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();
}
private
T setInitialValue() {
T value
=
initialValue();
Thread t
=
Thread.currentThread();
ThreadLocalMap map
=
getMap(t);
if
(map
!=
null
)
map.set(
this
, value);
else
createMap(t, value);
return
value;
}
protected
T initialValue() {
return
null
;
}
這樣,我們就明白上面的問題出在哪里:我們在main方法執行期間,試圖在調用ThreadDemo3的構造器時向ThreadLocal置入Student對象,而此時,以ThreadLocal對象為key,Student對象為value的Map是被放入當前的活動線程內的。也就是Main線程。而當我們的3個ThreadDemo3線程運行起來以后,調用get()方法,都是試圖從當前的活動線程中取得ThreadLocalMap對象,但當前的活動線程顯然已經不是Main線程了,于是,程序最終執行了ThreadLocal原生的initialValue()方法,返回了null。
轉載注明出處:http://x- spirit.javaeye.com/、http: //www.tkk7.com/zhangwei217245/
講到這里,我想不少朋友一定已經看出來了:ThreadLocal的initialValue()方法是需要被覆蓋的。
轉載注明出處:http://x- spirit.javaeye.com/、http: //www.tkk7.com/zhangwei217245/
于是,ThreadLocal的正確使用方法是:將ThreadLocal以內部類的形式進行繼承,并覆蓋原來的initialValue()方法,在這里產生可供線程擁有的本地變量值。
這樣,我們就有了下面的正確例程:
/**
*
*
@author
x-spirit
*/
public
class
ThreadDemo3
extends
Thread{
private
ThreadLocal
<
Student
>
stuLocal
=
new
ThreadLocal
<
Student
>
(){
@Override
protected
Student initialValue() {
return
new
Student();
}
};
public
ThreadDemo3(){
}
public
static
void
main(String[] args) {
ThreadDemo3 td31
=
new
ThreadDemo3();
ThreadDemo3 td32
=
new
ThreadDemo3();
ThreadDemo3 td33
=
new
ThreadDemo3();
td31.start();
td32.start();
td33.start();
}
@Override
public
void
run() {
accessStudent();
}
public
void
accessStudent() {
String currentThreadName
=
Thread.currentThread().getName();
System.out.println(currentThreadName
+
"
is running!
"
);
Random random
=
new
Random();
int
age
=
random.nextInt(
100
);
System.out.println(
"
thread
"
+
currentThreadName
+
"
set age to:
"
+
age);
Student student
=
stuLocal.get();
student.setAge(age);
System.out.println(
"
thread
"
+
currentThreadName
+
"
first read age is:
"
+
student.getAge());
try
{
Thread.sleep(
5000
);
}
catch
(InterruptedException ex) {
ex.printStackTrace();
}
System.out.println(
"
thread
"
+
currentThreadName
+
"
second read age is:
"
+
student.getAge());
}
}
********** 補疑 ******************
有的童鞋可能會問:“你這個Demo根本沒體現出來,每個線程里都有一個ThreadLocal對象;應該是一個ThreadLocal對象對應多個線程,你這變成了一對一,完全沒體現出ThreadLocal的作用。”
那么我們來看一下如何用一個ThreadLocal對象來對應多個線程:
/** */
/**
*
*
@author
x-spirit
*/
public
class
ThreadDemo3
implements
Runnable
{
private
ThreadLocal
<
Student
>
stuLocal
=
new
ThreadLocal
<
Student
>
()
{
@Override
protected
Student initialValue()
{
return
new
Student();
}
}
;
public
ThreadDemo3()
{
}
public
static
void
main(String[] args)
{
ThreadDemo3 td3
=
new
ThreadDemo3();
Thread t1
=
new
Thread(td3);
Thread t2
=
new
Thread(td3);
Thread t3
=
new
Thread(td3);
t1.start();
t2.start();
t3.start();
}
@Override
public
void
run()
{
accessStudent();
}
public
void
accessStudent()
{
String currentThreadName
=
Thread.currentThread().getName();
System.out.println(currentThreadName
+
"
is running!
"
);
Random random
=
new
Random();
int
age
=
random.nextInt(
100
);
System.out.println(
"
thread
"
+
currentThreadName
+
"
set age to:
"
+
age);
Student student
=
stuLocal.get();
student.setAge(age);
System.out.println(
"
thread
"
+
currentThreadName
+
"
first read age is:
"
+
student.getAge());
try
{
Thread.sleep(
5000
);
}
catch
(InterruptedException ex)
{
ex.printStackTrace();
}
System.out.println(
"
thread
"
+
currentThreadName
+
"
second read age is:
"
+
student.getAge());
}
}
這里,多個線程對象都使用同一個實現了Runnable接口的ThreadDemo3對象來構造。這樣,多個線程使用的ThreadLocal對象就是同一個。結果仍然是正確的。但是仔細回想一下,這兩種實現方案有什么不同呢?
答案其實很簡單,并沒有本質上的不同。對于第一種實現,不同的線程對象當中ThreadLocalMap里面的KEY使用的是不同的ThreadLocal對象。而對于第二種實現,不同的線程對象當中ThreadLocalMap里面的KEY是同一個ThreadLocal對象。但是從本質上講,不同的線程對象都是利用其自身的ThreadLocalMap對象來對各自的Student對象進行封裝,用ThreadLocal對象作為該ThreadLocalMap的KEY。所以說,“ThreadLocal的思想精髓就是為每個線程創建獨立的資源副本。”這句話并不應當被理解成:一定要使用同一個ThreadLocal對象來對多個線程進行處理。因為真正用來封裝變量的不是ThreadLocal。就算是你的程序中所有線程都共用同一個ThreadLocal對象,而你真正封裝到ThreadLocalMap中去的仍然是.hashCode()方法返回不同值的不同對象。就好比線程就是房東,ThreadLocalMap就是房東的房子。房東通過ThreadLocal這個中介去和房子里的房客打交道,而房東不管要讓房客住進去還是搬出來,都首先要經過ThreadLocal這個中介。
所以提到ThreadLocal,我們不應當顧名思義的認為JDK里面提供ThreadLocal就是提供了一個用來封裝本地線程存儲的容器,它本身并沒有Map那樣的容器功能。真正發揮作用的是ThreadLocalMap。也就是說,事實上,采用ThreadLocal來提高并發行,首先要理解,這不是一種簡單的對象封裝,而是一套機制,而這套機制中的三個關鍵因素(Thread、ThreadLocal、ThreadLocalMap)之間的關系是值得我們引起注意的。
**************** 補疑完畢 ***************************
可見,要正確使用ThreadLocal,必須注意以下幾點:
轉載注明出處:http://x- spirit.javaeye.com/、http: //www.tkk7.com/zhangwei217245/
1. 總是對ThreadLocal中的initialValue()方法進行覆蓋。
轉載注明出處:http://x- spirit.javaeye.com/、http: //www.tkk7.com/zhangwei217245/
2. 當使用set()或get()方法時牢記這兩個方法是對當前活動線程中的ThreadLocalMap進行操作,一定要認清哪個是當前活動線程!
轉載注明出處:http://x- spirit.javaeye.com/、http: //www.tkk7.com/zhangwei217245/
3. 適當的使用泛型,可以減少不必要的類型轉換以及可能由此產生的問題。
轉載注明出處:http://x- spirit.javaeye.com/、http: //www.tkk7.com/zhangwei217245/
運行該程序,我們發現:程序的執行過程只需要5秒,而如果采用同步的方法,程序的執行結果相同,但執行時間需要15秒。以前是多個線程為了爭取一個資源,不得不在同步規則的制約下互相謙讓,浪費了一些時間。
轉載注明出處:http://x- spirit.javaeye.com/、http: //www.tkk7.com/zhangwei217245/
現在,采用ThreadLocal機制以后,可用的資源多了,你有我有全都有,所以,每個線程都可以毫無顧忌的工作,自然就提高了并發性,線程安全也得以保證。
當今很多流行的開源框架也采用ThreadLocal機制來解決線程的并發問題。比如大名鼎鼎的 Struts 2.x 和 Spring 等。
把ThreadLocal這樣的話題放在我們的同步機制探討中似乎顯得不是很合適。但是ThreadLocal的確為我們解決多線程的并發問題帶來了全新的思路。它為每個線程創建一個獨立的資源副本,從而將多個線程中的數據隔離開來,避免了同步所產生的性能問題,是一種“以空間換時間”的解決方案。
但這并不是說ThreadLocal就是包治百病的萬能藥了。如果實際的情況不允許我們為每個線程分配一個本地資源副本的話,同步還是非常有意義的。
轉載注明出處:http://x- spirit.javaeye.com/、http: //www.tkk7.com/zhangwei217245/
好了,本系列到此馬上就要劃上一個圓滿的句號了。不知大家有什么意見和疑問沒有。希望看到你們的留言。
下一講中我們就來對之前的內容進行一個總結,順便討論一下被遺忘的volatile關鍵字。敬請期待。
發表于 2010-04-24 13:36
X-Spirit
閱讀(8410)
評論(15)
編輯
收藏
所屬分類:
Java SE
評論
#
re: Java 多線程同步問題的探究(五、你有我有全都有—— ThreadLocal如何解決并發安全性?)
"總是對ThreadLocal中的initialValue()方法進行覆蓋" ThreadLocal貌似不是這么用的,你現在為每個線程都 new 一個對象當然不會沖突,干脆在 ThreadDemo3 的構造方法中 new 好了,使用 ThreadLocal 干嘛?
pwl2014
評論于 2010-04-08 09:53
回復
更多評論
#
re: Java 多線程同步問題的探究(五、你有我有全都有—— ThreadLocal如何解決并發安全性?)
@pwl2014
這只是一個DEMO,使用new在這里其實就是一個打比方的方法。如果我用StudentFactory也許更容易接受一點,呵呵。
在實際的應用中,我們經??梢钥吹揭恍┯肨hreadLocal來封裝資源的例子。他們無一例外的都是做一件事情:創建新的資源,供線程使用。例如用ThreadLocal來處理JDBC 的Connection。即使你沒有覆蓋initialValue()方法,而是用先get再判空,再set的方式,也還是為一個沒有獲取到connection的線程創建一個connection。
所以,問題的關鍵不在于ThreadLocal使用的時候采用何種形式。ThreadLocal的思想精髓就是為每個線程創建獨立的資源副本。而使用ThreadLocal的時候最最重要的就是分清楚當前活動線程是哪個。
至于覆蓋initialValue()方法的問題,這個應該是仁者見仁,智者見智的問題,我只是提出一個能夠節約代碼量的方案。一般情況下,覆蓋initialValue()方法已經可以解決問題,這是一種最為經濟的編碼習慣,它不僅能夠達到要求,并且和JDK的原生API結合的很好,不容易出錯,當然如果你需要在用ThreadLocal處理資源之前做一些其他的處理,那就另當別論了。
X-Spirit
評論于 2010-04-08 11:07
回復
更多評論
#
re: Java 多線程同步問題的探究(五、你有我有全都有—— ThreadLocal如何解決并發安全性?)
嗯,不錯!這一系列文章好。。。
anniezheng
評論于 2010-04-08 19:14
回復
更多評論
#
re: Java 多線程同步問題的探究(五、你有我有全都有—— ThreadLocal如何解決并發安全性?)
@anniezheng
謝謝夸獎啦
X-Spirit
評論于 2010-04-09 00:09
回復
更多評論
#
re: Java 多線程同步問題的探究(五、你有我有全都有—— ThreadLocal如何解決并發安全性?)
@X-Spirit
這句話說的好,ThreadLocal的思想精髓就是為每個線程創建獨立的資源副本。你這個Demo根本沒體現出來,每個線程里都有一個ThreadLocal對象;應該是一個ThreadLocal對象對應多個線程,你這變成了一對一,完全沒體現出ThreadLocal的作用。
路過
評論于 2010-04-23 13:42
回復
更多評論
#
re: Java 多線程同步問題的探究(五、你有我有全都有—— ThreadLocal如何解決并發安全性?)【更新重要補疑】
學習了!
boiledwater
評論于 2010-05-18 18:18
回復
更多評論
#
re: Java 多線程同步問題的探究(五、你有我有全都有—— ThreadLocal如何解決并發安全性?)【更新重要補疑】
最近在網上看些關于線程的東西,開始在一個論壇上找到轉載你的一片帖子,然后一路殺過來,找到了你的大本營,一口氣看了你寫這個6篇文章,感覺收獲挺大的,我之前學習了struts2,也發現了它用這個東西,一直沒有深究,在這里看到,覺得一下子發現了另一座山似的。但是我的疑問是ThreadLocal這個東西的使用場景,還是不太明確。
康博
評論于 2010-05-28 14:11
回復
更多評論
#
re: Java 多線程同步問題的探究(五、你有我有全都有—— ThreadLocal如何解決并發安全性?)【更新重要補疑】
@康博
感謝你的關注。關于應用場景的問題,我想沒有什么非常好的例子可以給你。不過只能告訴你使用ThreadLocal的好處就是可以避免同步帶來的性能損耗,并且,當多個線程同時使用同一個類的實例的時候,如果這個實例不是單例模式的一個實現,那么ThreadLocal就是值得考慮的。
X-Spirit
評論于 2010-06-07 00:54
回復
更多評論
#
re: Java 多線程同步問題的探究(五、你有我有全都有—— ThreadLocal如何解決并發安全性?)【更新重要補疑】
@X-Spirit
謝謝你的回復,期待你的下一篇文章。
康博
評論于 2010-06-12 15:23
回復
更多評論
#
re: Java 多線程同步問題的探究(五、你有我有全都有—— ThreadLocal如何解決并發安全性?)【更新重要補疑】
@康博
呵呵,這個系列基本上要結束了。但是由于我最近比較忙,所以本系列的總結還沒有時間整理。不過過些日子會整理出來的。請保持關注。
X-Spirit
評論于 2010-06-12 19:55
回復
更多評論
#
re: Java 多線程同步問題的探究(五、你有我有全都有—— ThreadLocal如何解決并發安全性?)【更新重要補疑】
支持技術貼,軟件也不錯
圣光永恒
評論于 2010-07-07 18:23
回復
更多評論
#
re: Java 多線程同步問題的探究(五、你有我有全都有—— ThreadLocal如何解決并發安全性?)【更新重要補疑】
技術的確的支持,頂一下
朱少
評論于 2010-07-08 16:37
回復
更多評論
#
re: Java 多線程同步問題的探究(五、你有我有全都有—— ThreadLocal如何解決并發安全性?)【更新重要補疑】
好全啊
謝謝樓主
nauxiaoyao
評論于 2010-07-13 19:19
回復
更多評論
#
re: Java 多線程同步問題的探究(五、你有我有全都有—— ThreadLocal如何解決并發安全性?)【更新重要補疑】
好詳細
了解了鎮面貌了
nauxiaoyao
評論于 2010-07-13 19:39
回復
更多評論
#
re: Java 多線程同步問題的探究(五、你有我有全都有—— ThreadLocal如何解決并發安全性?)【更新重要補疑】
不錯的編程 支持技術帖
朱少
評論于 2010-07-17 12:44
回復
更多評論
新用戶注冊
刷新評論列表
只有注冊用戶
登錄
后才能發表評論。
網站導航:
博客園
IT新聞
Chat2DB
C++博客
博問
管理
相關文章:
【Tech Details】【轉】有關Java SPI機制
【Effective】Logging最佳實踐
Java 多線程同步問題的探究(五、你有我有全都有—— ThreadLocal如何解決并發安全性?)【更新重要補疑】
Java 多線程同步問題的探究(四、協作,互斥下的協作——Java多線程協作(wait、notify、notifyAll))
Java 多線程同步問題的探究(三、Lock來了,大家都讓開【2. Fair or Unfair? It is a question...】)
Java 多線程同步問題的探究(三、Lock來了,大家都讓開【1. 認識重入鎖】)
Java 多線程同步問題的探究(二、給我一把鎖,我能創造一個規矩)
Java多線程同步問題的探究(一、線程的先來后到)
【Effective】JVM 調優參數說明
<
2010年4月
>
日
一
二
三
四
五
六
28
29
30
31
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
1
2
3
4
5
6
7
8
常用鏈接
我的隨筆
我的文章
我的評論
我的參與
最新評論
留言簿
(6)
給我留言
查看公開留言
查看私人留言
隨筆分類
(28)
Big Data
(rss)
Cloud
(rss)
Java EE
(rss)
Java FX
(rss)
Java SE(9)
(rss)
Java 輕量級企業開發(5)
(rss)
Linux(3)
(rss)
MySQL
(rss)
NetBeans(1)
(rss)
Node.js
(rss)
NoSQL
(rss)
Oracle(3)
(rss)
前端技術
(rss)
開源協議(1)
(rss)
思維模式
(rss)
悟(1)
(rss)
技術之外(5)
(rss)
敏捷開發
(rss)
時間管理
(rss)
隨筆檔案
(90)
2014年9月 (1)
2014年3月 (1)
2014年2月 (1)
2014年1月 (1)
2013年2月 (1)
2012年11月 (1)
2012年10月 (1)
2012年3月 (1)
2012年2月 (1)
2011年2月 (1)
2010年4月 (6)
2010年3月 (4)
2010年1月 (2)
2009年11月 (1)
2009年8月 (1)
2009年7月 (1)
2009年6月 (2)
2009年5月 (1)
2009年4月 (13)
2009年3月 (1)
2009年2月 (1)
2009年1月 (2)
2008年12月 (3)
2008年11月 (1)
2008年10月 (1)
2008年9月 (3)
2008年5月 (1)
2008年4月 (9)
2008年3月 (3)
2008年1月 (1)
2007年12月 (4)
2007年10月 (2)
2007年9月 (6)
2007年8月 (9)
2007年7月 (1)
2007年4月 (1)
文章分類
(1)
My Voice(1)
(rss)
文章檔案
(1)
2009年12月 (1)
收藏夾
(4)
別人的學習筆記(4)
(rss)
牛人牛博
DaoRu的Blog
(rss)
愛折騰的道儒
Doug Lea's Home Page
Doug Lea's Home Page
jolestar
老王的博客
Tim Yang
(rss)
后端技術
Yang_net
(rss)
靠譜IT帥哥
搖擺巴赫@blog.sina
源碼控
搖擺巴赫@javaeye
源碼控
文初的一畝三分地
淘寶放翁
淘寶核心團隊
淘寶核心團隊
福林雨
(rss)
福林的博客
那誰的BLOG
那誰的BLOG
酷站
ImportNew
學習新知、發現新朋友
InfoQ中文站
InfoQ中文站
InfoQ英文站
InfoQ英文站
Java Code Geeks
A website for Java Geeks
并發編程網
并發編程網
開源中國
OSCHINA
百度技術沙龍
百度技術沙龍
酷殼
(rss)
酷殼
最新隨筆
1.?【Math's History】什么是羅素悖論
2.?【Effective】IntelliJ IDEA MAC IDE config files
3.?5 Ways To Burn Out Programming
4.?【Efficiency】快速配置ubuntu桌面環境之Java環境配置[全軟件源安裝]
5.?【Efficiency】MAC下使用設定可以從mission control中啟動的eclipse.app。
6.?【Effective】如何遷移git倉庫
7.?【轉】閱讀我們的學科——計算機專業學習淺談
8.?【Tech Details】【轉】有關Java SPI機制
9.?【Efficiency】 監控 Linux 性能的 18 個命令行工具
10.?【Effective】Logging最佳實踐
搜索
最新評論
1.?re: Java 多線程同步問題的探究(三、Lock來了,大家都讓開【1. 認識重入鎖】)
上班
--地點
2.?re: 【轉】閱讀我們的學科——計算機專業學習淺談
好文!
--何楊
3.?re: [導入][轉]重復提交、重復刷新、防止后退的問題以及處理方式
是多少
--乒乓、
4.?......
....
--..
5.?re: Java多線程同步問題的探究(一、線程的先來后到)
多線程這塊一直是軟肋,學習一下,呵呵
--FlyingFish
閱讀排行榜
1.?Java 多線程同步問題的探究(三、Lock來了,大家都讓開【1. 認識重入鎖】)(9053)
2.?Java 多線程同步問題的探究(五、你有我有全都有—— ThreadLocal如何解決并發安全性?)【更新重要補疑】(8410)
3.?Java 多線程同步問題的探究(二、給我一把鎖,我能創造一個規矩)(7440)
4.?Java 多線程同步問題的探究(四、協作,互斥下的協作——Java多線程協作(wait、notify、notifyAll))(7251)
5.?Java多線程同步問題的探究(一、線程的先來后到)(7102)
評論排行榜
1.?Java 多線程同步問題的探究(五、你有我有全都有—— ThreadLocal如何解決并發安全性?)【更新重要補疑】(15)
2.?Java 多線程同步問題的探究(三、Lock來了,大家都讓開【1. 認識重入鎖】)(10)
3.?Java 多線程同步問題的探究(二、給我一把鎖,我能創造一個規矩)(9)
4.?Java 多線程同步問題的探究(四、協作,互斥下的協作——Java多線程協作(wait、notify、notifyAll))(9)
5.?Java多線程同步問題的探究(一、線程的先來后到)(8)
Powered by:
博客園
模板提供:
滬江博客
Copyright ©2025 X-Spirit
主站蜘蛛池模板:
亚洲成AV人片在线观看ww
|
亚洲国产AV一区二区三区四区
|
免费无码又爽又刺激网站
|
亚洲av成人一区二区三区在线观看
|
国产成人免费在线
|
久久精品国产亚洲av影院
|
日本免费中文字幕
|
亚洲精品福利视频
|
99热在线精品免费播放6
|
久久久久亚洲精品无码蜜桃
|
最近2019年免费中文字幕高清
|
精品亚洲aⅴ在线观看
|
精品女同一区二区三区免费站
|
91亚洲自偷在线观看国产馆
|
无码国产精品一区二区免费式直播
|
亚洲美女一区二区三区
|
在线观看成人免费视频不卡
|
亚洲人成图片网站
|
免费无码又爽又高潮视频
|
亚洲高清在线mv
|
亚洲免费观看在线视频
|
ass亚洲**毛茸茸pics
|
大陆一级毛片免费视频观看
|
久久精品国产亚洲AV未满十八
|
免费人成在线观看播放国产
|
一级做a爰片久久毛片免费看
|
亚洲色无码一区二区三区
|
日韩精品免费视频
|
亚洲国产精品人久久电影
|
性xxxx视频播放免费
|
国产尤物在线视精品在亚洲
|
久久亚洲国产精品五月天婷
|
秋霞人成在线观看免费视频
|
亚洲a在线视频视频
|
99精品一区二区免费视频
|
亚洲香蕉久久一区二区
|
日本特黄特色免费大片
|
人人鲁免费播放视频人人香蕉
|
国产亚洲精品xxx
|
嘿嘿嘿视频免费网站在线观看
|
亚洲日本VA中文字幕久久道具
|