一、例子
package com.ruigege.LockSourceAnalysis6;
import java.util.concurrent.locks.LockSupport;
public class TestParkAndUnpark {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("child thread begin park");
LockSupport.park();
System.out.println("child thread end park");
System.out.println("今天又學了一個快捷鍵,sysout + alt +/ 是控制檯" +
"輸出的一個快捷鍵");
}
});
thread.start();
Thread.sleep(1000);
System.out.println("main thread begin unpark");
LockSupport.unpark(thread);
}
}
- 首先建立了一個子線程,然後調用park方法,由於默認情況下,子線程沒有持有許可證,因此它會把自己掛起;在主線程中執行了unpark方法,參數爲子線程,這樣做的目的就是讓子線程能夠持有許可證,然後子線程調用的park方法就會返回
- 注意點:park方法不會告訴我們是因爲哪種原因返回的,因此調用者需要根據之前調用park方法的原因,再次檢查條件是否滿足,如果不滿足的話,還需再次調用park方法
- 例如:根據調用前後的中斷狀態的對比可以判斷是不是因爲被中斷才返回的。
- 下面爲了說明調用park方法後的線程是因爲被中斷才返回的,我們修改代碼
package com.ruigege.LockSourceAnalysis6;
import java.util.concurrent.locks.LockSupport;
public class TestParkAndUnpark {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("child thread begin park");
while(!Thread.currentThread().isInterrupted()) {
LockSupport.park();
}
System.out.println("child thread end park");
System.out.println("今天又學了一個快捷鍵,sysout + alt +/ 是控制檯" +
"輸出的一個快捷鍵");
}
});
thread.start();
Thread.sleep(1000);
System.out.println("main thread begin unpark");
thread.interrupt();
}
}
- 我們可以從中看出,如果只有中斷了子線程,子線程纔會運行結束,如果子線程不中斷的話,即使調用了LockSupport(thread)方法,也不會中斷。
二、void parkNanos(long nanos)方法
- 與park方法相類似,如果該線程沒有拿到許可證,那麼調用parkNanos(long nanos)方法該線程會立即停止阻塞,並返回;如果有許可證,那麼nanos毫秒之後,該線程纔會返回。
package com.ruigege.LockSourceAnalysis6;
import java.util.concurrent.locks.LockSupport;
public class TestPark {
public void testPark() {
LockSupport.park();
}
public static void main(String[] args) {
System.out.println("開始park方法");
TestPark testPark = new TestPark();
testPark.testPark();
}
}
- 下面是我們使用parkNanos方法來代替LockSupport.park()方法
LockSupport.park(this);
- 使用這個帶參數的park(Object blocker)方法,當線程在沒有持有許可證的時候,調用park方法,會被阻塞起來,這個blocker對象會被記錄到該線程的內部。
- 使用jstack pid命令可以對線程堆棧進行查看,該線程內部是含有的什麼對象
三、park(Object blocker)源碼解析
public static void park2(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t,blocker);
UNSAFE.park(false,0L);
setBlocker(t,null);
}
四、void parkNanos(Object blocker,long nanos)方法
五、void parkUtil(Object blocker,long deadline)方法
- 這個方法和parkNanos不同的就是超時時間的算法,parkNanos的超時時間是從線程阻塞開始算起的,而parkUtil方法的超時時間是從1970年開始算起,到某一個時間點的毫秒數
public static void parkUtile(Object blocker,long deadline) {
Thread t = Thread.currentThread();
setBlocker(t,blocker);
UNSAFE.park(false,deadline);
setBlocker(t,null);
}
六、下面再看一個例子
package com.ruigege.LockSourceAnalysis6;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;
public class FIFOMutex {
private final AtomicBoolean locked = new AtomicBoolean(false);
private final Queue<Thread> waiters = new ConcurrentLinkedQueue<Thread>();
public void lock() {
boolean wasInterrupted = false;
Thread current = Thread.currentThread();
waiters.add(current);
while(waiters.peek() != current || !locked.compareAndSet(false,true)) {
LockeSupport.park(this);
if(Thread.interrupted()) {
wasInterrupted = true;
}
}
waiters.remove();
if(wasInterrupted) {
current.interrupt();
}
}
public void unlock() {
locked.set(false);
LockSupport.unpark(waiters.peek());
}
}
- 這是一個先進先出的鎖,也就是隻有隊列的首元素可以獲取鎖,在代碼(1)如果當前線程不是隊首或者當前鎖已經被其他線程獲取,那麼調用park方法掛起自己。
- 然後再代碼(2)處做判斷,如果park方法是因爲被中斷而返回的,則忽略中斷,並且重置中斷標誌,複習該方法去
- 在代碼(3)中,判斷標記,如果標記爲true那麼中斷該線程
- 總結:其實就是其他線程中斷了該線程,雖然我對中斷信號不感興趣,忽略它(也就是代碼(2)),但是不代表其他線程對該標誌不感興趣,我們還需要恢復一下。
七、源碼:
- 所在包:com.ruigege.ConcurrentListSouceCodeAnalysis5
https://github.com/ruigege66/ConcurrentJava
- 歡迎關注微信公衆號:傅里葉變換,個人賬號,僅用於技術交流