您好,登錄后才能下訂單哦!
本篇內容介紹了“LockSupport的原理和作用是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
用于創建鎖和其他同步類的基本線程阻塞原語。
使用它,可以用來構建一些特定邏輯的鎖。jdk并發包的AQS框架就依賴了它。
它提供了線程的阻塞和喚醒。
每當一個線程使用它,那么就可以當作這個線程維持了一個二元信號量,可以比作持有許可證。
調用pack()
方法,如果有許可證,那么消耗它并且直接返回,線程繼續執行下去。反之,則阻塞。
調用unpack()
方法,如果許可證不可用,那么就變成可用,如果是可用,那么就不做其他操作。也就是說,許可證不可以多次累加。
因為有許可證這種中間介質,也就避免了調用線程不推薦的Thread.suspend()
和Thread.resume()
而導致的線程失活的竟態情況。這里我個人的理解是,線程調度的復雜性和延遲。比如說:線程的休眠和暫停并不能夠做到即可馬上、命令之間的重排等等。并且,park()
也額外支持了線程打斷和超時功能。
類的文檔里面額外提及了一個慣用法,說的是pack()
調用會"無理由"返回,從而導致線程莫名的執行下去。那么慣用法就是把pack()
邏輯包裝在一個循環當中,并且循環退出的條件就是線程等待同步許可的條件。這就相當于一個自旋的優化,只是需要unpack()
配合。
一個阻塞線程的簡單范例:
public static void main(String[] args) { LockSupport.park(); System.out.println("主線程阻塞,并不會打印當前消息"); }
一個通用的使用范式:
public static void main(String[] args) throws InterruptedException { Thread busThreadJim = new Thread(() -> { System.out.println("參數檢查..."); System.out.println("執行業務邏輯..."); System.out.println("進入狀態同步點"); LockSupport.park("因為可能原因A而阻塞住線程"); System.out.println("同步點完成,進步業務收尾階段..."); System.out.println("進入最后一個同步點結束"); LockSupport.park("報告這個線程當前的工作程度或者狀態xxxx"); System.out.println("業務邏輯完成退出"); }); busThreadJim.start(); System.out.println("控制線程或者監控線程開始做其他邏輯..."); Thread.sleep(2000); System.out.println("檢查工作線程是否阻塞住,為什么:" + LockSupport.getBlocker(busThreadJim)); LockSupport.unpark(busThreadJim); // 如果線程調度慢 LockSupport.getBlocker獲取到null // 也就是說工作線程還沒有阻塞住 System.out.println("可以通過阻塞對象來傳出一些線程工作信息:" + LockSupport.getBlocker(busThreadJim)); System.out.println("當然共享變量也是可以的"); LockSupport.unpark(busThreadJim); System.out.println("邏輯完成"); }
多次unpack()
也只能消耗一次
public static void main(String[] args) throws InterruptedException { Thread worker = new Thread(() -> { System.out.println("執行業務邏輯..."); System.out.println("進入狀態同步點..."); LockSupport.park(); System.out.println("同步點完成,進步業務收尾階段..."); LockSupport.park(); System.out.println("最后一個同步點結束"); }); worker.start(); LockSupport.unpark(worker); LockSupport.unpark(worker); Thread.sleep(2000); LockSupport.unpark(worker); }
設計代碼邏輯的時候,盡量有明確的阻塞條件,然后選擇帶有時間截至的函數和提供阻塞信息的對象。
理論上來說,應該用不到這個類。但是一旦用到了,估計就是設計很基礎的東西,上面肯定有復雜的邏輯。沒有阻塞信息到時候邏輯排查是要命的。
wait/notify必須在同步代碼塊中使用,光這一點就感覺到限制比較大。
wait/notify操作的是對象,然后才能映射到對象所在的線程,思考方式有點饒。
復雜的邏輯,特別是嵌套,特別窒息。
public class LockSupport { private LockSupport() {} // 該類無法實例化 // 所有的功能都代理給內部來實現 private static final sun.misc.Unsafe UNSAFE; // 把當前線程阻塞住 public static void park() { UNSAFE.park(false, 0L); } // 把當前執行線程與一個阻塞對象相關連 // 這樣子關注對象就變成線程而非對象 public static void park(Object blocker) { Thread t = Thread.currentThread(); setBlocker(t, blocker); UNSAFE.park(false, 0L); setBlocker(t, null); } // 下面就是一系列時間相關函數 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); } } public static void parkUntil(Object blocker, long deadline) { Thread t = Thread.currentThread(); setBlocker(t, blocker); UNSAFE.park(true, deadline); setBlocker(t, null); } public static void parkNanos(long nanos) { if (nanos > 0) UNSAFE.park(false, nanos); } public static void parkUntil(long deadline) { UNSAFE.park(true, deadline); } // 喚醒目標也是線程 跟阻塞關注點相同 public static void unpark(Thread thread) { if (thread != null) UNSAFE.unpark(thread); } // 返回阻塞對象信息 // 如果頻繁的pack,那么該對象可能一直在變更,它的生命周期會比較短,只是最近一次阻塞的信息 public static Object getBlocker(Thread t) { if (t == null) throw new NullPointerException(); return UNSAFE.getObjectVolatile(t, parkBlockerOffset); } }
值得注意的關注點在于blocker的信息獲取,直接操作內存,因為線程已經阻塞住,只能通過這種機制來獲取阻塞線程內信息。
“LockSupport的原理和作用是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。