胡厚崑否認華為將新增 “養豬”等業務傳聞
javadoc
用於建立鎖和其他同步類的基本執行緒阻塞原語。
這個類與每個使用它的執行緒相關聯,一個許可證(在Semaphore
類的意義上)。如果許可證可用,則呼叫park
將park
返回,在此過程中消耗它;否則可能會阻止。
呼叫unpark
使許可證可用,如果尚不可用。(與訊號量不同,許可證不能累積,最多隻有一個。)
方法park
和unpark
提供了阻止和解除阻塞執行緒的有效手段,該方法不會遇到導致不推薦使用的方法Thread.suspend
和Thread.resume
目的不能使用的問題:
一個執行緒呼叫park
和另一個執行緒之間的嘗試unpark
執行緒將保持活躍性,由於許可證。另外,如果呼叫者的執行緒被中斷,park
park
方法也可以在任何其他時間返回,因為“無理由”,因此一般必須在返回之前重新檢查條件的迴圈中被呼叫。在這個意義上,park
作為一個“忙碌等待”的優化,不浪費時間旋轉,但必須與unpark
配對才能有效。
park
的三種形式也支援blocker
物件引數。執行緒被阻止時記錄此物件(blocker--
LockSupport的一個屬性),以允許監視和診斷工具識別執行緒被阻止的原因。(此類工具可以使用方法getBlocker(Thread)
訪問阻止程式。)強烈鼓勵使用這些形式而不是沒有此引數的原始形式。在鎖實現中作為blocker
提供的正常引數是this
。
這些方法被設計為用作建立更高階同步實用程式的工具,並且本身對於大多數併發控制應用程式本身並不有用。park
樣板用例:
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); // 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()); } }
中斷的響應性
class LockSupportTest {
public static void t2() throws Exception {
Thread t = new Thread(new Runnable() {
private int count = 0;
@Override
public void run() {
long start = System.currentTimeMillis();
long end = 0;
while ((end - start) <= 1000) {
count++;
end = System.currentTimeMillis();
}
System.out.println("after 1 second.count=" + count);
//等待或許許可
LockSupport.park();
System.out.println("thread over." + Thread.currentThread().isInterrupted());
}
});
t.start();
Thread.sleep(2000);
// 中斷執行緒
t.interrupt();
System.out.println("main over");
}
結果:
after 1 second.count=286194848
main over
thread over.true
結論:
中斷執行緒(t.interrupt())可以起到釋放uppark的作用.
不會丟擲:InterruptedException異常
原始碼
來看一下LockSupport原始碼,其中有兩個屬性unsafe 和 parkBlokcerOffset
// Hotspot implementation via intrinsics API
private static final sun.misc.Unsafe UNSAFE;
private static final long parkBlockerOffset;
private static final long SEED;
private static final long PROBE;
private static final long SECONDARY;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> tk = Thread.class;
parkBlockerOffset = UNSAFE.objectFieldOffset
(tk.getDeclaredField("parkBlocker"));
SEED = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSeed"));
PROBE = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomProbe"));
SECONDARY = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSecondarySeed"));
} catch (Exception ex) { throw new Error(ex); }
}
unsafe:是JDK內部用的工具類。它通過暴露一些Java意義上說“不安全”的功能給Java層程式碼,來讓JDK能夠更多的使用Java程式碼來實現一些原本是平臺相關的、需要使用native語言(例如C或C++)才可以實現的功能。該類不應該在JDK核心類庫之外使用。
parkBlokcerOffset:parkBlocker的偏移量,讓我們來看看Thread類的實現:
parkBlocker是用於記錄執行緒是被誰阻塞的。可以通過LockSupport的getBlocker獲取到阻塞的物件。用於監控和分析執行緒用的。
偏移量就算Thread這個類裡面變數parkBlocker在記憶體中的偏移量,直接通過記憶體偏移量給變數賦值.
為什麼要用偏移量來獲取物件?幹嗎不要直接寫個get,set方法。多簡單?
仔細想想就能明白,這個parkBlocker就是線上程處於阻塞的情況下才會被賦值。執行緒都已經阻塞了,如果不通過這種記憶體的方法,而是直接呼叫執行緒內的方法,執行緒是不會迴應呼叫的。
Unsafe記憶體操作
public class UnsafeDemo {
private int i = 0;
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
//獲取Unsafe例項
Field f = Unsafe.class.getDeclaredField("theUnsafe"); // Internal reference
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
//獲取欄位i在記憶體中偏移量
long offset = unsafe.objectFieldOffset(UnsafeDemo.class.getDeclaredField("i"));
//建立物件例項,設定欄位的值
UnsafeDemo unsafeDemo = new UnsafeDemo();
unsafe.putInt(unsafeDemo, offset, 100);
//列印結果
System.out.println(unsafeDemo.i);
}
}
說明
JVM的實現可以自由選擇如何實現Java物件的“佈局”,也就是在記憶體裡Java物件的各個部分放在哪裡,包括物件的例項欄位和一些元資料之類。
sun.misc.Unsafe裡關於物件欄位訪問的方法把物件佈局抽象出來,它提供了objectFieldOffset()方法用於獲取某個欄位相對Java物件的“起始地址”的偏移量,
也提供了getInt、getLong、getObject之類的方法可以使用前面獲取的偏移量來訪問某個Java物件的某個欄位。
在上例中,我們通過putInt方法給一個int變數i賦值,類似的,Unsafe也提供了putLong、putFloat、putDouble、putChar、putByte、putShort、putBoolean、以及putObject等方法給對應型別的變數賦值。
並提供了相應的get方法。
參考:
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/LockSupport.html