摘要:此對象在線程受阻塞時被記錄,以允許監視工具和診斷工具確定線程受阻塞的原因。阻塞當前線程,最長不超過納秒,返回條件在的基礎上增加了超時返回。喚醒線程喚醒處于阻塞狀態的線程。
LockSupport 用法簡介
LockSupport 和 CAS 是Java并發包中很多并發工具控制機制的基礎,它們底層其實都是依賴Unsafe實現。
LockSupport是用來創建鎖和其他同步類的基本線程阻塞原語。LockSupport 提供park()和unpark()方法實現阻塞線程和解除線程阻塞,LockSupport和每個使用它的線程都與一個許可(permit)關聯。permit相當于1,0的開關,默認是0,調用一次unpark就加1變成1,調用一次park會消費permit, 也就是將1變成0,同時park立即返回。再次調用park會變成block(因為permit為0了,會阻塞在這里,直到permit變為1), 這時調用unpark會把permit置為1。每個線程都有一個相關的permit, permit最多只有一個,重復調用unpark也不會積累。
park()和unpark()不會有 “Thread.suspend和Thread.resume所可能引發的死鎖” 問題,由于許可的存在,調用 park 的線程和另一個試圖將其 unpark 的線程之間的競爭將保持活性。
如果調用線程被中斷,則park方法會返回。同時park也擁有可以設置超時時間的版本。
需要特別注意的一點:park 方法還可以在其他任何時間“毫無理由”地返回,因此通常必須在重新檢查返回條件的循環里調用此方法。從這個意義上說,park 是“忙碌等待”的一種優化,它不會浪費這么多的時間進行自旋,但是必須將它與 unpark 配對使用才更高效。
三種形式的 park 還各自支持一個 blocker 對象參數。此對象在線程受阻塞時被記錄,以允許監視工具和診斷工具確定線程受阻塞的原因。(這樣的工具可以使用方法 getBlocker(java.lang.Thread) 訪問 blocker。)建議最好使用這些形式,而不是不帶此參數的原始形式。在鎖實現中提供的作為 blocker 的普通參數是 this。
看下線程dump的結果來理解blocker的作用。
從線程dump結果可以看出:
有blocker的可以傳遞給開發人員更多的現場信息,可以查看到當前線程的阻塞對象,方便定位問題。所以java6新增加帶blocker入參的系列park方法,替代原有的park方法。
看一個Java docs中的示例用法:一個先進先出非重入鎖類的框架
class FIFOMutex { private final AtomicBoolean locked = new AtomicBoolean(false); private final QueueLockSupport 源碼解讀waiters = new ConcurrentLinkedQueue (); public void lock() { boolean wasInterrupted = false; Thread current = Thread.currentThread(); waiters.add(current); // Block while not first in queue or cannot acquire lock while (waiters.peek() != current || !locked.compareAndSet(false, true)) { LockSupport.park(this); if (Thread.interrupted()) // ignore interrupts while waiting wasInterrupted = true; } waiters.remove(); if (wasInterrupted) // reassert interrupt status on exit current.interrupt(); } public void unlock() { locked.set(false); LockSupport.unpark(waiters.peek()); } }}
LockSupport中主要的兩個成員變量:
// Hotspot implementation via intrinsics API private static final sun.misc.Unsafe UNSAFE; private static final long parkBlockerOffset;
unsafe:全名sun.misc.Unsafe可以直接操控內存,被JDK廣泛用于自己的包中,如java.nio和java.util.concurrent。但是不建議在生產環境中使用這個類。因為這個API十分不安全、不輕便、而且不穩定。
LockSupport的方法底層都是調用Unsafe的方法實現。
再來看parkBlockerOffset:
parkBlocker就是第一部分說到的用于記錄線程被誰阻塞的,用于線程監控和分析工具來定位原因的,可以通過LockSupport的getBlocker獲取到阻塞的對象。
static { try { UNSAFE = sun.misc.Unsafe.getUnsafe(); Class> tk = Thread.class; parkBlockerOffset = UNSAFE.objectFieldOffset (tk.getDeclaredField("parkBlocker")); } catch (Exception ex) { throw new Error(ex); } }
從這個靜態語句塊可以看的出來,先是通過反射機制獲取Thread類的parkBlocker字段對象。然后通過sun.misc.Unsafe對象的objectFieldOffset方法獲取到parkBlocker在內存里的偏移量,parkBlockerOffset的值就是這么來的.
JVM的實現可以自由選擇如何實現Java對象的“布局”,也就是在內存里Java對象的各個部分放在哪里,包括對象的實例字段和一些元數據之類。 sun.misc.Unsafe里關于對象字段訪問的方法把對象布局抽象出來,它提供了objectFieldOffset()方法用于獲取某個字段相對 Java對象的“起始地址”的偏移量,也提供了getInt、getLong、getObject之類的方法可以使用前面獲取的偏移量來訪問某個Java 對象的某個字段。
為什么要用偏移量來獲取對象?干嗎不要直接寫個get,set方法。多簡單?
仔細想想就能明白,這個parkBlocker就是在線程處于阻塞的情況下才會被賦值。線程都已經阻塞了,如果不通過這種內存的方法,而是直接調用線程內的方法,線程是不會回應調用的。
2.LockSupport的方法:
可以看到,LockSupport中主要是park和unpark方法以及設置和讀取parkBlocker方法。
private static void setBlocker(Thread t, Object arg) { // Even though volatile, hotspot doesn"t need a write barrier here. UNSAFE.putObject(t, parkBlockerOffset, arg); }
對給定線程t的parkBlocker賦值。
public static Object getBlocker(Thread t) { if (t == null) throw new NullPointerException(); return UNSAFE.getObjectVolatile(t, parkBlockerOffset); }
從線程t中獲取它的parkBlocker對象,即返回的是阻塞線程t的Blocker對象。
接下來主查兩類方法,一類是阻塞park方法,一類是解除阻塞unpark方法
阻塞線程
park()
public static void park() { UNSAFE.park(false, 0L); }
調用native方法阻塞當前線程。
parkNanos(long nanos)
public static void parkNanos(long nanos) { if (nanos > 0) UNSAFE.park(false, nanos); }
阻塞當前線程,最長不超過nanos納秒,返回條件在park()的基礎上增加了超時返回。
parkUntil(long deadline)
public static void parkUntil(long deadline) { UNSAFE.park(true, deadline); }
阻塞當前線程,知道deadline時間(deadline - 毫秒數)。
JDK1.6引入這三個方法對應的擁有Blocker版本。
park(Object blocker)
public static void park(Object blocker) { Thread t = Thread.currentThread(); setBlocker(t, blocker); UNSAFE.park(false, 0L); setBlocker(t, null); }
1) 記錄當前線程等待的對象(阻塞對象);
2) 阻塞當前線程;
3) 當前線程等待對象置為null。
parkNanos(Object blocker, long nanos)
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); } }
阻塞當前線程,最長等待時間不超過nanos毫秒,同樣,在阻塞當前線程的時候做了記錄當前線程等待的對象操作。
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); }
阻塞當前線程直到deadline時間,相同的,也做了阻塞前記錄當前線程等待對象的操作。
喚醒線程
unpark(Thread thread)
public static void unpark(Thread thread) { if (thread != null) UNSAFE.unpark(thread); }
喚醒處于阻塞狀態的線程Thread。
參考:
http://tool.oschina.net/apido...
http://ifeve.com/locksuppor-s...
http://www.jianshu.com/p/ceb8...
http://15838341661-139-com.it...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/66694.html
摘要:基礎問題的的性能及原理之區別詳解備忘筆記深入理解流水線抽象關鍵字修飾符知識點總結必看篇中的關鍵字解析回調機制解讀抽象類與三大特征時間和時間戳的相互轉換為什么要使用內部類對象鎖和類鎖的區別,,優缺點及比較提高篇八詳解內部類單例模式和 Java基礎問題 String的+的性能及原理 java之yield(),sleep(),wait()區別詳解-備忘筆記 深入理解Java Stream流水...
摘要:基礎問題的的性能及原理之區別詳解備忘筆記深入理解流水線抽象關鍵字修飾符知識點總結必看篇中的關鍵字解析回調機制解讀抽象類與三大特征時間和時間戳的相互轉換為什么要使用內部類對象鎖和類鎖的區別,,優缺點及比較提高篇八詳解內部類單例模式和 Java基礎問題 String的+的性能及原理 java之yield(),sleep(),wait()區別詳解-備忘筆記 深入理解Java Stream流水...
摘要:基礎問題的的性能及原理之區別詳解備忘筆記深入理解流水線抽象關鍵字修飾符知識點總結必看篇中的關鍵字解析回調機制解讀抽象類與三大特征時間和時間戳的相互轉換為什么要使用內部類對象鎖和類鎖的區別,,優缺點及比較提高篇八詳解內部類單例模式和 Java基礎問題 String的+的性能及原理 java之yield(),sleep(),wait()區別詳解-備忘筆記 深入理解Java Stream流水...
摘要:線程安全的概念什么時候線程不安全怎樣做到線程安全怎么擴展線程安全的類對線程安全的支持對線程安全支持有哪些中的線程池的使用與中線程池的生命周期與線程中斷中的鎖中常見死鎖與活鎖的實例線程同步機制顯示鎖使用與原理原理剖析原理中的與原理偏向鎖狀態 showImg(https://segmentfault.com/img/bVblUE9?w=1354&h=1660); 線程安全的概念 showI...
閱讀 1289·2023-04-25 19:33
閱讀 1171·2021-10-21 09:39
閱讀 3644·2021-09-09 09:32
閱讀 2614·2019-08-30 10:58
閱讀 1599·2019-08-29 16:17
閱讀 873·2019-08-29 15:29
閱讀 2885·2019-08-26 11:55
閱讀 2658·2019-08-26 10:33