LockSupport运吓,構(gòu)建同步組件的基礎(chǔ)工具渴邦,幫AQS完成相應(yīng)線程的阻塞或者喚醒的工作。
LockSupport源碼分析
LockSupport定義了一組以park開(kāi)頭的方法來(lái)阻塞當(dāng)前線程拘哨,unpark來(lái)喚醒被阻塞的線程谋梭。
阻塞線程
- park()實(shí)現(xiàn)
public static void park() {
UNSAFE.park(false, 0L);
}
調(diào)用native方法阻塞當(dāng)前線程。
- parkNanos(long nanos)實(shí)現(xiàn)
public static void parkNanos(long nanos) {
if (nanos > 0)
UNSAFE.park(false, nanos);
}
阻塞當(dāng)前線程宅静,最長(zhǎng)不超過(guò)nanos納秒章蚣,返回條件在park()的基礎(chǔ)上增加了超時(shí)返回。
- parkUntil(long deadline)實(shí)現(xiàn)
public static void parkUntil(long deadline) {
UNSAFE.park(true, deadline);
}
阻塞當(dāng)前線程姨夹,知道deadline時(shí)間(deadline - 毫秒數(shù))纤垂。
在java6之后在park系列方法新增加了入?yún)?code>Object blocker,用于標(biāo)識(shí)阻塞對(duì)象磷账,該對(duì)象主要用于問(wèn)題排查和系統(tǒng)監(jiān)控峭沦。
- park(Object blocker)實(shí)現(xiàn)
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
記錄當(dāng)前線程等待的對(duì)象(阻塞對(duì)象);
阻塞當(dāng)前線程逃糟;
當(dāng)前線程等待對(duì)象置為null吼鱼。
parkNanos(Object blocker, long nanos)實(shí)現(xiàn)
public static void parkNanos(Object blocker, long nanos) {
if (nanos > 0) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, nanos);
setBlocker(t, null);
}
}
阻塞當(dāng)前線程,最長(zhǎng)等待時(shí)間不超過(guò)nanos毫秒绰咽,同樣菇肃,在阻塞當(dāng)前線程的時(shí)候做了記錄當(dāng)前線程等待的對(duì)象操作。
- parkUntil(Object blocker, long deadline)
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(true, deadline);
setBlocker(t, null);
}
阻塞當(dāng)前線程直到deadline時(shí)間取募,相同的琐谤,也做了阻塞前記錄當(dāng)前線程等待對(duì)象的操作。
到這里玩敏,問(wèn)題來(lái)了斗忌,為什么在java6要在入?yún)⒁隻locker呢?blocker的作用到底是什么旺聚?
先看看線程dump的結(jié)果:
從線程dump結(jié)果可以看出:
有blocker的可以傳遞給開(kāi)發(fā)人員更多的現(xiàn)場(chǎng)信息织阳,可以查看到當(dāng)前線程的阻塞對(duì)象,方便定位問(wèn)題砰粹。所以java6新增加帶blocker入?yún)⒌南盗衟ark方法唧躲,替代原有的park方法。
喚醒線程
- unpark(Thread thread)實(shí)現(xiàn)
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
喚醒處于阻塞狀態(tài)的線程Thread。
從源碼不難發(fā)現(xiàn)惊窖,LockSupport所有的方法都是調(diào)用native的park和unpark實(shí)現(xiàn)的刽宪,接下來(lái)我們具體看看HotSpot中的park/unpark的具體實(shí)現(xiàn)。
HotSpot里的park/unpark
在HotSpot中界酒,每個(gè)java線程都有一個(gè)Parker的實(shí)例圣拄,Parker的定義是這樣子的:
從Parker定義不難看出:
定義私有屬性_counter:可以理解為是否可以調(diào)用park的一個(gè)許可證,只有_count > 0的時(shí)候才能調(diào)用毁欣;
提供public方法park和unpark支撐阻塞/喚醒線程庇谆;
-
Parker繼承PlatformParker:
PlatformParker定義
從PlatformParker的定義可以看出,Parker實(shí)際上是利用Posix的mutex凭疮,condition來(lái)實(shí)現(xiàn)的饭耳。
HotSpot park實(shí)現(xiàn)
-
嘗試是否能可以調(diào)用park,如果_counter > 0执解,可以調(diào)用寞肖,將_counter置為0,返回衰腌;
park步驟1具體實(shí)現(xiàn) -
步驟1不成功新蟆,構(gòu)造當(dāng)前線程的ThreadBlockInVM,檢查_(kāi)counter > 0是否成立右蕊,成立則將_counter設(shè)置為0琼稻,unlock mutex,返回饶囚;
park步驟2具體實(shí)現(xiàn) -
步驟2不成功帕翻,根據(jù)等待時(shí)間調(diào)用不同的等待函數(shù)等待,如果等待返回正確萝风,將_counter置為0嘀掸,unlock mutex,返回规惰,park調(diào)用成功横殴。
park步驟3具體實(shí)現(xiàn)
相比之下,unpark的實(shí)現(xiàn)就簡(jiǎn)單多了卿拴。
HotSpot unpark實(shí)現(xiàn)
- 將_counter置為1;
- 判斷之前_counter的值:
- 小于1時(shí)梨与,調(diào)用pthread_cond_signal喚醒在park中等待的線程堕花;
- 等于1時(shí),unlock mutex粥鞋,返回缘挽。
總結(jié):HotSpot Parker用condition和mutex維護(hù)了一個(gè)_counter變量,park時(shí),變量_counter置為0壕曼,unpark時(shí)苏研,變量_counter置為1。