一、例子
下面來一個例子加深對park和unpark的理解
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");
//呼叫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); // 呼叫unpark方法,能夠讓子執行緒thread持有許可證,
// 然后park方法回傳
}
}
下面來解釋一下這個類的主要功效 首先建立了一個子執行緒,然后呼叫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");
//呼叫park方法,掛起自己
// LockSupport.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");
// LockSupport.unpark(thread); // 呼叫unpark方法,能夠讓子執行緒thread持有許可證,
// 然后park方法回傳
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();
// Thread物件中有一個volatile Object blocker
// 這里呼叫setter方法,把這個blocker記錄到該執行緒的blocker物件中
setBlocker(t,blocker);
// 呼叫park方法對該執行緒進行阻塞
UNSAFE.park(false,0L); // UNSAFE其實是該執行緒的Unsafe變數,我們
// 這里省略前面的定義,直接拿來解釋
setBlocker(t,null);
// 最后我們又把blocker物件置為空,這是因為已經停止阻塞了
// 這個blocker物件多用于執行緒阻塞的時候用來分析原因用的
}
基本都寫在了方法的解釋之后
四、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); // 一個boolean類的鎖
private final Queue<Thread> waiters = new ConcurrentLinkedQueue<Thread>(); // 一個高并發佇列
public void lock() {
boolean wasInterrupted = false; // 中斷的標志
Thread current = Thread.currentThread();
waiters.add(current); // 佇列中添加這個執行緒
// (1)
while(waiters.peek() != current || !locked.compareAndSet(false,true)) {
// 復習compareAndSet方法,第一個引數是期盼的值,第二個就是如果就是期盼的值,那么就
// 設定為第二個引數,然后回傳true
LockeSupport.park(this);
if(Thread.interrupted()) { // (2)
wasInterrupted = true;
}
}
waiters.remove();
if(wasInterrupted) { // (3)
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/ConcurrentJavaCSDN:https://blog.csdn.net/weixin_44630050 博客園:https://www.cnblogs.com/ruigege0000/ 歡迎關注微信公眾號:傅里葉變換,個人賬號,僅用于技術交流 
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/251370.html
標籤:Java
