Exchanger原始碼解析
Exchanger
(交換者)是一個用於執行緒間協作的工具類。Exchanger
用於進行執行緒間的資料交換。它提供一個同步點,在這個同步點,兩個執行緒可以交換彼此的資料。這兩個執行緒通過exchange
方法交換資料,如果第一個執行緒先執行 exchange()
方法,它會一直等待第二個執行緒也執行 exchange
方法,當兩個執行緒都到達同步點時,這兩個執行緒就可以交換資料,將本執行緒生產出來的資料傳遞給對方。
// 用於左移Node陣列下標,以便得出陣列在記憶體中偏移量來獲取資料,避免偽共享
private static final int ASHIFT = 7;
// Node陣列最大下標
private static final int MMASK = 0xff;
// 用於遞增bound,每次遞增一個SEQ
private static final int SEQ = MMASK + 1;
// CPU核心數
private static final int NCPU = Runtime.getRuntime().availableProcessors();
// 當前陣列最大下標
static final int FULL = (NCPU >= (MMASK << 1)) ? MMASK : NCPU >>> 1;
// 自旋次數,CPU核心為1時自旋會被禁用
private static final int SPINS = 1 << 10;
// 用於exchange方法中引數為null時傳遞給其它執行緒的物件
private static final Object NULL_ITEM = new Object();
// 用於超時傳遞的物件
private static final Object TIMED_OUT = new Object();
// 節點用於保持需要交換的資料
@sun.misc.Contended static final class Node {
int index; // arana陣列下標,多槽位時使用
int bound; // 上一次記錄的bound
int collides; // CAS失敗次數
int hash; // 用於自旋的偽隨機數
Object item; // 當前執行緒需要交換的資料
volatile Object match; // 匹配執行緒交換的資料
volatile Thread parked; // 記錄當前掛起的執行緒
}
// 使用者記錄執行緒狀態的內部類
static final class Participant extends ThreadLocal<Node> {
public Node initialValue() { return new Node(); }
}
// 記錄執行緒狀態
private final Participant participant;
// 多槽位資料交換使用
private volatile Node[] arena;
// 用於交換資料的槽位
private volatile Node slot;
/**
* The index of the largest valid arena position, OR'ed with SEQ
* number in high bits, incremented on each update. The initial
* update from 0 to SEQ is used to ensure that the arena array is
* constructed only once.
*/
private volatile int bound;
exchange
方法
public V exchange(V x) throws InterruptedException {
Object v;
Object item = (x == null) ? NULL_ITEM : x; // translate null args
if ((arena != null ||
(v = slotExchange(item, false, 0L)) == null) &&
((Thread.interrupted() || // disambiguates null return
(v = arenaExchange(item, false, 0L)) == null)))
throw new InterruptedException();
return (v == NULL_ITEM) ? null : (V)v;
}
private final Object slotExchange(Object item, boolean timed, long ns) {
Node p = participant.get(); // 獲取當前節點物件
Thread t = Thread.currentThread(); // 當前執行緒
// 執行緒中斷,直接返回null
if (t.isInterrupted()) // preserve interrupt status so caller can recheck
return null;
// 自旋
for (Node q;;) {
// 槽位slot不為null,則說明已經存線上程等待交換資料
if ((q = slot) != null) {
// CAS置空槽位slot
if (U.compareAndSwapObject(this, SLOT, q, null)) {
Object v = q.item; // 獲取槽位中需要交換的物件
q.match = item; // 將當前需要交換的資料設定到match中
Thread w = q.parked; // 獲取被掛起執行緒
// 存在掛起執行緒,則喚醒
if (w != null)
U.unpark(w);
return v; // 返回交換後的資料
}
// 存在競爭,其它執行緒搶先一步,需要使用多槽位交換方式
// CPU為多核心 且 bound等於0(arana陣列未初始化),則CAS操作將bound增加SEQ
if (NCPU > 1 && bound == 0 &&
U.compareAndSwapInt(this, BOUND, 0, SEQ))
arena = new Node[(FULL + 2) << ASHIFT]; // 初始化arana陣列
}
else if (arena != null) // 多槽位不為空,執行多槽位交換
return null; // caller must reroute to arenaExchange
else {
// 表示當前執行緒是第一個執行緒進來交換資料 或 之前交換任務已完成,可重新認為是第一個執行緒,
// 將需要交換的資料存放到槽位slot的item屬性
p.item = item;
// CAS設定槽位為p
if (U.compareAndSwapObject(this, SLOT, null, p))
break; // CAS操作成功結束自旋
p.item = null; // CAS設定槽位失敗,置空item,繼續自旋操作
}
}
// 當前執行緒已經佔據槽位,等待其它執行緒交換資料
int h = p.hash;
long end = timed ? System.nanoTime() + ns : 0L;
int spins = (NCPU > 1) ? SPINS : 1; // 自旋次數
Object v;
// 其它執行緒成功交換槽位中資料
while ((v = p.match) == null) {
if (spins > 0) { // 自旋
h ^= h << 1; h ^= h >>> 3; h ^= h << 10;
if (h == 0)
h = SPINS | (int)t.getId();
else if (h < 0 && (--spins & ((SPINS >>> 1) - 1)) == 0)
Thread.yield(); // 執行緒讓步,提供CPU利用率
}
// 存線上程交換資料,已修改槽位slot,但未修改match屬性,則等待
else if (slot != p)
spins = SPINS;
// 執行緒未中斷 且 不是多槽位交換 且 (沒有設定超時 或 超時時間未到)
else if (!t.isInterrupted() && arena == null &&
(!timed || (ns = end - System.nanoTime()) > 0L)) {
U.putObject(t, BLOCKER, this); // 設定執行緒t被當前物件阻塞
p.parked = t; // 設定節點掛起執行緒屬性parked
// 如果槽位slot不等於null,表明還沒有執行緒與之交換資料,則將當前執行緒掛起
if (slot == p)
U.park(false, ns);
p.parked = null; // 執行緒被喚醒,將節點掛起執行緒parked屬性設定為null
U.putObject(t, BLOCKER, null); // 設定執行緒t沒有被任何物件阻塞
}
// 不滿足上述新增,交換失敗,重置槽位slot
else if (U.compareAndSwapObject(this, SLOT, p, null)) {
v = timed && ns <= 0L && !t.isInterrupted() ? TIMED_OUT : null;
break;
}
}
U.putOrderedObject(p, MATCH, null); // 置空match
p.item = null; // 置空item
p.hash = h;
return v; // 返回交換資料
}
相關文章
- 原始碼分析:Exchanger之資料交換器原始碼
- 併發程式設計之 Exchanger 原始碼分析程式設計原始碼
- CountDownLatch、CyclicBarrier、Semaphore、Exchanger 的詳細解析CountDownLatch
- 【原始碼解析】- ArrayList原始碼解析,絕對詳細原始碼
- Spark原始碼-SparkContext原始碼解析Spark原始碼Context
- CountDownLatch原始碼解析CountDownLatch原始碼
- LeakCanary原始碼解析原始碼
- vuex原始碼解析Vue原始碼
- ArrayBlockQueue原始碼解析BloC原始碼
- AsyncTask原始碼解析原始碼
- CopyOnWriteArrayList原始碼解析原始碼
- Express原始碼解析Express原始碼
- Observer原始碼解析Server原始碼
- SparseArray 原始碼解析原始碼
- RecyclerView原始碼解析View原始碼
- Promise 原始碼解析Promise原始碼
- Koa原始碼解析原始碼
- RateLimiter原始碼解析MIT原始碼
- redux原始碼解析Redux原始碼
- SDWebImage原始碼解析Web原始碼
- CyclicBarrier原始碼解析原始碼
- Semaphore原始碼解析原始碼
- AbstractQueuedSynchronizer原始碼解析原始碼
- OKio原始碼解析原始碼
- Koa 原始碼解析原始碼
- RxPermission原始碼解析原始碼
- MyBatis原始碼解析MyBatis原始碼
- ArrayList原始碼解析原始碼
- Aspects 原始碼解析原始碼
- LeakCanary 原始碼解析原始碼
- Vue原始碼解析Vue原始碼
- React原始碼解析React原始碼
- ButterKnife原始碼解析原始碼
- HashSet原始碼解析原始碼
- Retrofit 原始碼解析原始碼
- Javapoet原始碼解析Java原始碼
- Vuex 原始碼解析Vue原始碼
- OKHttp原始碼解析HTTP原始碼