【java源碼學習-多線程】LockSupport(一),javalocksupport
今天開始學習Java多線程源碼,並將學習到的東西記錄下來,由於個人能力有限,文中難免有錯誤之處,希望看到我部落格的園友能指出我的錯誤,在此表示感謝!同時希望自己能堅持下來,加油!
學習java多線程需要先瞭解一些原理性的東西,比如記憶體可見度,原子性,重排序,Java記憶體模型等等,由於是在學習Java多線程源碼,所以我不會寫這些東西,等以後有機會再寫成部落格,這個系列主要學習java.util.concurrent包下的源碼,首先學習java中的鎖,也就是java.util.concurrent.locks下的類,是locks包下得類結構:
這個包下最核心的類是AbstractQueuedSynchronizer類,這個類就是實現同步器,Java中鎖的實現都是通過內聚一個同步器來實現的;所以學習Java的鎖,首先需要學習同步器的作用和原理,在學習同步器之前,先學習LockSupport類,這個類中的方法主要用於阻塞線程和喚醒線程。
一、類中的成員變數
1 // Unsafe成員變數 2 private static final sun.misc.Unsafe UNSAFE; 3 // Thread類的parkBlocker的位移地址 4 private static final long parkBlockerOffset; 5 // Thread類的threadLocalRandomSeed的位移地址 6 private static final long SEED; 7 // Thread類的threadLocalRandomProbe的位移地址 8 private static final long PROBE; 9 // Thread類的threadLocalRandomSecondarySeed的位移地址10 private static final long SECONDARY;11 // 以上long類型的都是記憶體位移地址,通過該位移地址就可以擷取或設定該值12 static {13 try {14 UNSAFE = sun.misc.Unsafe.getUnsafe();15 Class<?> tk = Thread.class;16 // UNSAFE.objectFieldOffset用於擷取欄位的位移地址17 parkBlockerOffset = UNSAFE.objectFieldOffset18 (tk.getDeclaredField("parkBlocker"));19 SEED = UNSAFE.objectFieldOffset20 (tk.getDeclaredField("threadLocalRandomSeed"));21 PROBE = UNSAFE.objectFieldOffset22 (tk.getDeclaredField("threadLocalRandomProbe"));23 SECONDARY = UNSAFE.objectFieldOffset24 (tk.getDeclaredField("threadLocalRandomSecondarySeed"));25 } catch (Exception ex) { throw new Error(ex); }26 }
每一個線程(Thread)都會有parkBlocker等成員變數,其中parkBlocker表示線程阻塞在哪個對象上,在分析問題時可以通過列印該成員變數來查看線程阻塞在哪個對象上,其餘幾個變數的含義不太清楚;這裡通過UNSAFE.objectFieldOffset可以擷取欄位相對於線程對象的記憶體位移,通過這個記憶體位移可以快速的擷取或者設定該成員變數的值。
二、阻塞線程的方法
1 // 擷取線程t的parkBlocker 2 public static Object getBlocker(Thread t) { 3 if (t == null) 4 throw new NullPointerException(); 5 return UNSAFE.getObjectVolatile(t, parkBlockerOffset); 6 } 7 8 // 設定線程t的parkBlocker欄位的值為arg 9 private static void setBlocker(Thread t, Object arg) {10 // parkBlockerOffset為parkBlocker的記憶體位移11 UNSAFE.putObject(t, parkBlockerOffset, arg);12 }13 14 public static void park() {15 // UNISAFE.part方法用於阻塞線程,第一個參數表示是否為絕對時間,第二個參數為0表示無限等待16 UNSAFE.park(false, 0L);17 }18 19 public static void park(Object blocker) {20 Thread t = Thread.currentThread();21 // 設定線程t的blocker欄位22 setBlocker(t, blocker);23 // 阻塞線程,所以下面的setBlocker不會接著執行24 UNSAFE.park(false, 0L);25 // 只有當調用unpark方法或者線程被中斷時才會執行下面第二個setBlocker方法26 setBlocker(t, null);27 }28 29 // nanos為逾時時間30 public static void parkNanos(long nanos) {31 if (nanos > 0)32 UNSAFE.park(false, nanos);33 }34 35 public static void parkNanos(Object blocker, long nanos) {36 if (nanos > 0) {37 Thread t = Thread.currentThread();38 setBlocker(t, blocker);39 UNSAFE.park(false, nanos);40 setBlocker(t, null);41 }42 }43 44 public static void parkUntil(long deadline) {45 // 這裡使用的時絕對時間46 UNSAFE.park(true, deadline);47 }48 49 public static void parkUntil(Object blocker, long deadline) {50 Thread t = Thread.currentThread();51 setBlocker(t, blocker);52 UNSAFE.park(true, deadline);53 setBlocker(t, null);54 }
阻塞線程的方法可以分兩大類,一類是不設定parkBlocker的,一類是設定parkBlocker的。而每一類有三種方法:一種是永久阻塞線程,直到線程被喚醒或者被中斷;一種是增加了逾時時間,使用的是相對時間;一種是增加了逾時時間,但使用的是絕對時間。
三、喚醒線程的方法
1 public static void unpark(Thread thread) {2 if (thread != null)3 // UNSAFE.unpark用於喚醒線程,但使用該方法必須保證傳入的thread線程必須存在4 UNSAFE.unpark(thread);5 }
在LockSupport類中還有一個方法nextSecondarySeed,這個方法用於產生隨機數,但不清楚有什麼用處,有知道的園友希望能告訴我。