中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

托管型網(wǎng)站專業(yè)網(wǎng)絡(luò)推廣公司排名

托管型網(wǎng)站,專業(yè)網(wǎng)絡(luò)推廣公司排名,招商網(wǎng)站建設(shè)需要什么,免費(fèi)申請?jiān)囉镁W(wǎng)站ConcurrentSkipListMap的源碼很復(fù)雜,但它的核心思想和實(shí)現(xiàn)方式非常值得深入學(xué)習(xí)。下面會提煉出它的關(guān)鍵結(jié)構(gòu),并重點(diǎn)解釋最復(fù)雜、最值得學(xué)習(xí)的方法,以及它在并發(fā)和跳表結(jié)合上的獨(dú)特用法。 1. 關(guān)鍵結(jié)構(gòu) 跳表(Skip List)…

ConcurrentSkipListMap的源碼很復(fù)雜,但它的核心思想和實(shí)現(xiàn)方式非常值得深入學(xué)習(xí)。下面會提煉出它的關(guān)鍵結(jié)構(gòu),并重點(diǎn)解釋最復(fù)雜、最值得學(xué)習(xí)的方法,以及它在并發(fā)和跳表結(jié)合上的獨(dú)特用法。


1. 關(guān)鍵結(jié)構(gòu)

跳表(Skip List)基本結(jié)構(gòu)

  • Node<K,V>:跳表的基礎(chǔ)節(jié)點(diǎn),存儲key、value和指向下一個節(jié)點(diǎn)的指針(next)。
  • Index<K,V>:跳表多級索引節(jié)點(diǎn),存儲指向下方索引(down)和右側(cè)索引(right)的指針,每個Index節(jié)點(diǎn)關(guān)聯(lián)一個Node節(jié)點(diǎn)。
  • head:頂層索引節(jié)點(diǎn)(Index類型),通過down可以訪問到底層。
static final class Node<K,V> {final K key;V val;Node<K,V> next;...
}
static final class Index<K,V> {final Node<K,V> node;final Index<K,V> down;Index<K,V> right;...
}

其他核心成員

  • comparator:可選的比較器,決定排序規(guī)則。
  • LongAdder adder:高效計(jì)數(shù)器,記錄元素數(shù)量。
  • VarHandle機(jī)制:用來做原子操作,支持無鎖CAS(Compare and Swap)修改指針和數(shù)據(jù)。

2. 并發(fā)跳表的獨(dú)特實(shí)現(xiàn)

跳表的多層索引

  • 跳表結(jié)構(gòu)使得查找、插入、刪除的平均復(fù)雜度為O(log n),而且天然適合并發(fā)遍歷。

  • 索引節(jié)點(diǎn)(Index)和底層節(jié)點(diǎn)(Node)分離,每次變更都只需CAS原子操作,避免全局鎖。

無鎖設(shè)計(jì)(Lock-Free)

  • 主要依賴CAS原子操作(比如 NEXT.compareAndSet、VAL.compareAndSet),避免傳統(tǒng)鎖帶來的性能瓶頸。
  • 節(jié)點(diǎn)刪除采用“懶惰刪除”+“幫助刪除”,即遇到被標(biāo)記為刪除的節(jié)點(diǎn)時,順便幫忙物理移除,所有線程協(xié)作完成清理。

doPut(插入/更新元素)

這是插入和更新的核心方法,難點(diǎn)在于:

  • 多線程場景下,如何安全地插入節(jié)點(diǎn)和建立索引;
  • 如何在概率意義下決定新節(jié)點(diǎn)的高度(索引層數(shù));
  • 如何處理并發(fā)插入沖突和失敗的重試。

簡化流程:

  1. 找到插入位置的前驅(qū)節(jié)點(diǎn)(findPredecessor)。
  2. 通過CAS原子操作插入新節(jié)點(diǎn);
  3. 按概率(1/4)決定是否生成索引節(jié)點(diǎn),并逐層向上插入索引(addIndices);
  4. 若有必要,增加跳表高度。

doPut?方法是?ConcurrentSkipListMap?的核心插入方法,實(shí)現(xiàn)了線程安全的鍵值對插入操作。以下是該方法的詳細(xì)分析:

1. 方法簽名與參數(shù)說明

private V doPut(K key, V value, boolean onlyIfAbsent)
  • key: 要插入的鍵
  • value: 要插入的值
  • onlyIfAbsent: 如果為?true,僅在鍵不存在時插入;如果為?false,則替換已存在的值
  • 返回值: 如果是新插入返回?null,如果是替換則返回舊值

整體執(zhí)行流程

第一階段:初始化檢查與跳表遍歷

if (key == null)throw new NullPointerException();
Comparator<? super K> cmp = comparator;
for (;;) {  // 無限循環(huán),直到操作成功Index<K,V> h; Node<K,V> b;VarHandle.acquireFence(); // 內(nèi)存屏障,確??梢娦?

關(guān)鍵知識點(diǎn):

  • 使用?無限循環(huán)?實(shí)現(xiàn)?樂觀鎖?機(jī)制
  • VarHandle.acquireFence()?提供?內(nèi)存可見性保證,類似于 volatile 讀操作

第二階段:跳表初始化

if ((h = head) == null) {          // 跳表未初始化Node<K,V> base = new Node<K,V>(null, null, null);  // 創(chuàng)建哨兵節(jié)點(diǎn)h = new Index<K,V>(base, null, null);               // 創(chuàng)建頂層索引b = (HEAD.compareAndSet(this, null, h)) ? base : null; // CAS 操作
}

設(shè)計(jì)要點(diǎn):

  • 使用?哨兵節(jié)點(diǎn)?簡化邊界處理
  • 通過?CAS 操作?保證線程安全的初始化
  • 如果 CAS 失敗,說明其他線程已完成初始化,重新開始循環(huán)

第三階段:跳表索引層遍歷

for (Index<K,V> q = h, r, d;;) { // 從頂層開始遍歷while ((r = q.right) != null) {  // 水平向右遍歷Node<K,V> p; K k;if ((p = r.node) == null || (k = p.key) == null || p.val == null)RIGHT.compareAndSet(q, r, r.right); // 清理已刪除節(jié)點(diǎn)else if (cpr(cmp, key, k) > 0)q = r; // 繼續(xù)向右elsebreak; // 找到插入位置}if ((d = q.down) != null) {++levels; // 記錄下降層數(shù)q = d;    // 向下一層}else {b = q.node; // 找到基礎(chǔ)層的前驅(qū)節(jié)點(diǎn)break;}
}

跳表遍歷原理:

  • 水平遍歷:在當(dāng)前層級向右查找,直到找到大于等于目標(biāo)鍵的位置
  • 垂直下降:當(dāng)無法繼續(xù)右移時,下降到下一層級
  • 清理機(jī)制:遍歷過程中清理已標(biāo)記刪除的節(jié)點(diǎn),維護(hù)數(shù)據(jù)結(jié)構(gòu)完整性

基礎(chǔ)層節(jié)點(diǎn)插入

尋找精確插入位置

for (;;) {  // 在基礎(chǔ)層尋找插入點(diǎn)Node<K,V> n, p; K k; V v; int c;if ((n = b.next) == null) {if (b.key == null)  // 空跳表,進(jìn)行類型檢查cpr(cmp, key, key);c = -1;}else if ((k = n.key) == null)break;  // 遇到標(biāo)記節(jié)點(diǎn),重新開始else if ((v = n.val) == null) {unlinkNode(b, n);  // 清理已刪除節(jié)點(diǎn)c = 1;}else if ((c = cpr(cmp, key, k)) > 0)b = n;  // 繼續(xù)向右查找else if (c == 0 && (onlyIfAbsent || VAL.compareAndSet(n, v, value)))return v;  // 找到相同鍵,根據(jù) onlyIfAbsent 決定是否更新

執(zhí)行 CAS 插入

if (c < 0 && NEXT.compareAndSet(b, n, p = new Node<K,V>(key, value, n))) {z = p;  // 記錄新插入的節(jié)點(diǎn)break;
}

CAS 插入機(jī)制:

  • 原子性地將新節(jié)點(diǎn)插入到?b?和?n?之間
  • 如果 CAS 失敗,說明其他線程修改了鏈表結(jié)構(gòu),需要重新查找

來詳細(xì)解釋這段看似復(fù)雜的 null 處理邏輯。這些設(shè)計(jì)都與?ConcurrentSkipListMap 的并發(fā)刪除機(jī)制?密切相關(guān)。

1. 類型檢查邏輯分析

if (b.key == null)       // if empty, type check key nowcpr(cmp, key, key);

為什么需要這個檢查?

這個看似奇怪的?cpr(cmp, key, key)?調(diào)用實(shí)際上是一個?提前類型檢查

  • 觸發(fā)時機(jī):當(dāng)跳表為空時(b.key == null?表示?b?是哨兵節(jié)點(diǎn))
  • 目的:提前驗(yàn)證?key?的類型是否與比較器兼容
  • 機(jī)制:通過自比較觸發(fā)潛在的?ClassCastException
// cpr 方法的實(shí)現(xiàn)邏輯
static int cpr(Comparator c, Object x, Object y) {return (c != null) ? c.compare(x, y) : ((Comparable)x).compareTo(y);
}

如果?key?類型不兼容,這里會立即拋出異常,避免在后續(xù)操作中出現(xiàn)問題。

2. 并發(fā)刪除機(jī)制的核心區(qū)別

key == null?vs?val == null?的本質(zhì)區(qū)別

在 ConcurrentSkipListMap 中,這兩種 null 狀態(tài)代表?刪除過程的不同階段

狀態(tài)含義處理方式原因
key == null標(biāo)記節(jié)點(diǎn),表示前一個節(jié)點(diǎn)正在被刪除break?重新開始結(jié)構(gòu)不穩(wěn)定,無法安全插入
val == null邏輯刪除,節(jié)點(diǎn)已被標(biāo)記刪除但仍在鏈表中unlinkNode()?物理刪除可以安全地完成物理刪除

3. 并發(fā)刪除的三階段過程

根據(jù)代碼注釋,ConcurrentSkipListMap 采用三階段刪除:

階段 1:邏輯刪除

// 將節(jié)點(diǎn)值設(shè)為 null,標(biāo)記為已刪除
VAL.compareAndSet(n, v, null)

階段 2:插入標(biāo)記節(jié)點(diǎn)

// 插入一個 key=null 的標(biāo)記節(jié)點(diǎn)
NEXT.compareAndSet(n, f, new Node<K,V>(null, null, f))

階段 3:物理刪除

// 將前驅(qū)節(jié)點(diǎn)直接指向后繼節(jié)點(diǎn)
NEXT.compareAndSet(b, n, p)

代碼邏輯的并發(fā)安全考慮

else if ((k = n.key) == null)break;                   // can't append; restart
else if ((v = n.val) == null) {unlinkNode(b, n);c = 1;
}

為什么?key == null?需要重啟?

  • 結(jié)構(gòu)不穩(wěn)定:遇到標(biāo)記節(jié)點(diǎn)說明正在進(jìn)行刪除操作
  • 無法預(yù)測:不知道刪除何時完成,鏈表結(jié)構(gòu)可能隨時改變
  • 安全策略:重新開始遍歷,等待刪除操作完成

為什么?val == null?可以繼續(xù)?

  • 狀態(tài)確定:節(jié)點(diǎn)已被邏輯刪除,不會再被修改
  • 助人為樂:當(dāng)前線程可以幫助完成物理刪除
  • 性能優(yōu)化:避免其他線程重復(fù)遇到已刪除節(jié)點(diǎn)

實(shí)際執(zhí)行流程示例

假設(shè)有鏈表:A -> B -> C,要刪除節(jié)點(diǎn) B:

// 初始狀態(tài)
A.next -> B(key=b, val=v) -> C// 步驟1:邏輯刪除 B
A.next -> B(key=b, val=null) -> C
// 此時遇到 val==null,執(zhí)行 unlinkNode// 步驟2:插入標(biāo)記節(jié)點(diǎn)
A.next -> B(key=b, val=null) -> marker(key=null, val=null) -> C
// 此時遇到 key==null,執(zhí)行 break 重啟// 步驟3:物理刪除完成
A.next -> C

這種設(shè)計(jì)的巧妙在于:

  1. 漸進(jìn)式刪除:避免一次性操作導(dǎo)致的競爭
  2. 協(xié)作式清理:多個線程共同維護(hù)數(shù)據(jù)結(jié)構(gòu)完整性
  3. 無鎖設(shè)計(jì):通過 CAS 操作保證原子性
  4. 異常安全:提前進(jìn)行類型檢查,避免后續(xù)錯誤

這就是為什么同樣是 null,但處理方式完全不同的根本原因——它們代表了并發(fā)刪除過程中的不同狀態(tài),需要采用不同的應(yīng)對策略來保證線程安全。

索引層構(gòu)建(概率性)

隨機(jī)決定是否添加索引

int lr = ThreadLocalRandom.nextSecondarySeed();
if ((lr & 0x3) == 0) {  // 1/4 概率添加索引int hr = ThreadLocalRandom.nextSecondarySeed();long rnd = ((long)hr << 32) | ((long)lr & 0xffffffffL);int skips = levels;  // 從當(dāng)前層級開始構(gòu)建索引

構(gòu)建索引鏈

Index<K,V> x = null;
for (;;) {  // 構(gòu)建垂直索引鏈x = new Index<K,V>(z, x, null);  // 創(chuàng)建新索引節(jié)點(diǎn)if (rnd >= 0L || --skips < 0)break;elsernd <<= 1;  // 左移判斷下一層
}

跳表索引構(gòu)建原理:

  • 概率性構(gòu)建:每層索引節(jié)點(diǎn)數(shù)量約為下層的 1/2
  • 幾何分布:通過位運(yùn)算實(shí)現(xiàn)高效的概率計(jì)算
  • 最大層數(shù)限制:最多構(gòu)建 62 層索引

添加索引到跳表

if (addIndices(h, skips, x, cmp) && skips < 0 && head == h) {// 需要增加新的頂層Index<K,V> hx = new Index<K,V>(z, x, null);Index<K,V> nh = new Index<K,V>(h.node, h, hx);HEAD.compareAndSet(this, h, nh);
}

關(guān)鍵技術(shù)特性

并發(fā)安全機(jī)制

  1. Lock-Free 設(shè)計(jì):全程無鎖,使用 CAS 操作保證原子性

  2. 內(nèi)存屏障:使用?VarHandle.acquireFence()?確保內(nèi)存可見性

  3. 重試機(jī)制:失敗時重新開始,保證最終一致性

性能優(yōu)化策略

  1. 惰性刪除:標(biāo)記刪除而非立即物理刪除

  2. 清理機(jī)制:遍歷過程中順便清理已刪除節(jié)點(diǎn)

  3. 概率性索引:平衡查找效率和空間開銷

數(shù)據(jù)結(jié)構(gòu)維護(hù)

  1. 動態(tài)層級調(diào)整:根據(jù)需要增加新的索引層
  2. 索引一致性:確保索引層與基礎(chǔ)層數(shù)據(jù)一致
  3. 并發(fā)清理:在操作過程中維護(hù)結(jié)構(gòu)完整性

為什么需要標(biāo)記節(jié)點(diǎn)?并發(fā)鏈表刪除的根本問題

詳細(xì)解釋為什么ConcurrentSkipListMap不能"直接CAS刪除",而必須使用標(biāo)記節(jié)點(diǎn)的分步刪除策略。

如果我們嘗試"直接刪除":

// 危險的直接刪除方式
NEXT.compareAndSet(A, B, C);  // 直接讓A指向C

這會導(dǎo)致嚴(yán)重的并發(fā)競態(tài)條件

// 初始狀態(tài)
A -> B -> C -> D// 線程T1想刪除B,讀取了B.next = C
// 線程T2在B和C之間插入了新節(jié)點(diǎn)X
A -> B -> X -> C -> D// T1執(zhí)行CAS:A.next從B改為C
A -> C -> D  // X節(jié)點(diǎn)永遠(yuǎn)丟失!

unlinkNode的三步刪除策略

查看unlinkNode的實(shí)現(xiàn):

static <K,V> void unlinkNode(Node<K,V> b, Node<K,V> n) {if (b != null && n != null) {Node<K,V> f, p;for (;;) {if ((f = n.next) != null && f.key == null) {p = f.next;               // 步驟1:發(fā)現(xiàn)已有標(biāo)記節(jié)點(diǎn)break;}else if (NEXT.compareAndSet(n, f,new Node<K,V>(null, null, f))) {p = f;                    // 步驟2:插入標(biāo)記節(jié)點(diǎn)break;}}NEXT.compareAndSet(b, n, p);      // 步驟3:繞過被刪節(jié)點(diǎn)}
}

為什么分步刪除是必需的?

無鎖并發(fā)的本質(zhì)需求

  1. 原子性保證:每個CAS操作都是原子的,但組合操作不是
  2. 順序依賴:必須先"鎖定"被刪節(jié)點(diǎn)的next指針,再修改前驅(qū)節(jié)點(diǎn)
  3. 狀態(tài)可見性:中間狀態(tài)必須對所有線程可見和可處理

對比有鎖方案

// 如果用鎖(但ConcurrentSkipListMap是無鎖的)
synchronized(lock) {A.next = B.next;  // 可以原子完成
}

但無鎖環(huán)境下,我們只能依賴CAS的原子性,因此必須分解為原子步驟。

實(shí)際運(yùn)行示例

// 時刻T1:初始狀態(tài)
A.next -> B(key=5, val="hello") -> C(key=10)// 時刻T2:線程1開始刪除B,執(zhí)行邏輯刪除
A.next -> B(key=5, val=null) -> C(key=10)// 時刻T3:線程2試圖在B后插入節(jié)點(diǎn)失敗
// 因?yàn)榘l(fā)現(xiàn)B.val==null,執(zhí)行unlinkNode幫助刪除// 時刻T4:插入標(biāo)記節(jié)點(diǎn)
A.next -> B(key=5, val=null) -> marker(key=null, val=null) -> C(key=10)// 時刻T5:物理刪除完成
A.next -> C(key=10)

findPredecessor(查找前驅(qū)節(jié)點(diǎn))

  • 負(fù)責(zé)自頂向下查找,過程中順便幫忙清理已刪除節(jié)點(diǎn)的索引,保證跳表結(jié)構(gòu)整潔。
  • 支持并發(fā)刪除時的“協(xié)作清理”。

核心邏輯:

    private Node<K,V> findPredecessor(Object key, Comparator<? super K> cmp) {Index<K,V> q;VarHandle.acquireFence();if ((q = head) == null || key == null)return null;else {for (Index<K,V> r, d;;) {while ((r = q.right) != null) {Node<K,V> p; K k;if ((p = r.node) == null || (k = p.key) == null ||p.val == null)  // unlink index to deleted nodeRIGHT.compareAndSet(q, r, r.right);else if (cpr(cmp, key, k) > 0)q = r;elsebreak;}if ((d = q.down) != null)q = d;elsereturn q.node;}}}

VarHandle.acquireFence() 的作用

VarHandle.acquireFence()?在此處的作用是:

  1. 保證可見性:確保讀取到其他線程對?head?字段的最新修改
  2. 防止重排序:確保后續(xù)的讀操作基于一致的內(nèi)存狀態(tài)
  3. 性能優(yōu)化:避免使用過多 volatile 字段的開銷
  4. 維護(hù)弱一致性:為無鎖數(shù)據(jù)結(jié)構(gòu)提供必要的內(nèi)存語義保證

這是 ConcurrentSkipListMap 實(shí)現(xiàn)高性能并發(fā)訪問的關(guān)鍵技術(shù)之一。

  • acquireFence()確保在屏障之后的所有讀操作不會被重排序到屏障之前, 主要用于確保讀取操作能看到最新的寫入值,是構(gòu)建更高級并發(fā)原語的基礎(chǔ)工具。
  • releaseFence(): 確保屏障之前的所有寫操作對其他線程可見(類似于 StoreStore + LoadStore
  • fullFence(): 同時提供獲取和釋放語義(類似于 volatile 變量的讀寫)

內(nèi)存屏障的核心作用

1. 確保?head?字段的最新值可見

VarHandle.acquireFence();
if ((q = head) == null || key == null)return null;

關(guān)鍵問題head?字段可能被其他線程修改(如在?doPut、tryReduceLevel?等方法中),需要確保當(dāng)前線程能看到最新的值。

解決方案acquireFence()?提供了類似?volatile 讀?的語義,確保:

  • 讀取到?head?的最新值
  • 防止編譯器和處理器的指令重排序

2. 建立 happens-before 關(guān)系

根據(jù)代碼注釋中的設(shè)計(jì)說明:

This form of fence-hoisting is similar to RCU and related techniques... It minimizes overhead that may otherwise occur when using so many volatile-mode reads.

設(shè)計(jì)思路

  • 避免將每個字段都聲明為?volatile
  • 通過在關(guān)鍵方法入口處放置內(nèi)存屏障,建立全局的內(nèi)存可見性保證

為什么選擇 acquireFence 而非其他屏障

acquireFence 的特性

// acquireFence 等價于:
// 確保屏障之前的讀操作不會被重排序到屏障之后
// 提供 "acquire" 語義,類似于 volatile 讀或 synchronized 進(jìn)入

與 ConcurrentSkipListMap 的無鎖設(shè)計(jì)契合

  1. 無鎖遍歷findPredecessor?是純讀操作,不需要修改數(shù)據(jù)
  2. 弱一致性:允許看到稍微過時的數(shù)據(jù),但必須是一致的快照
  3. 性能優(yōu)化:比使用多個 volatile 字段開銷更小

場景分析

場景 1:讀取跳表頭節(jié)點(diǎn)

// 其他線程可能正在執(zhí)行:
HEAD.compareAndSet(this, h, nh);  // 在 doPut 中更新頭節(jié)點(diǎn)// 當(dāng)前線程需要確??吹阶钚碌?head 值
VarHandle.acquireFence();
if ((q = head) == null || key == null)  // 必須是最新值

場景 2:確保索引結(jié)構(gòu)一致性

// 確保讀取的索引鏈?zhǔn)且恢碌臓顟B(tài)
while ((r = q.right) != null) {Node<K,V> p; K k;// 這些字段的讀取都受到屏障保護(hù)if ((p = r.node) == null || (k = p.key) == null || p.val == null)

對比其他方法中的類似用法

可以看到相同的模式:

  1. doGet?方法
VarHandle.acquireFence();
if (key == null)throw new NullPointerException();
  1. findLast?方法
VarHandle.acquireFence();
if ((q = head) == null)break;

doRemove(刪除元素)

  • 先邏輯刪除(將val設(shè)為null),再插入一個“marker”節(jié)點(diǎn)做物理刪除,最后CAS更新前驅(qū)的next指針。
  • 刪除后會考慮是否需要降低跳表高度(tryReduceLevel)。
    /*** Main deletion method. Locates node, nulls value, appends a* deletion marker, unlinks predecessor, removes associated index* nodes, and possibly reduces head index level.** @param key the key* @param value if non-null, the value that must be* associated with key* @return the node, or null if not found*/final V doRemove(Object key, Object value) {if (key == null)throw new NullPointerException();Comparator<? super K> cmp = comparator;V result = null;Node<K,V> b;outer: while ((b = findPredecessor(key, cmp)) != null &&result == null) {for (;;) {Node<K,V> n; K k; V v; int c;if ((n = b.next) == null)break outer;else if ((k = n.key) == null)break;else if ((v = n.val) == null)unlinkNode(b, n);else if ((c = cpr(cmp, key, k)) > 0)b = n;else if (c < 0)break outer;else if (value != null && !value.equals(v))break outer;else if (VAL.compareAndSet(n, v, null)) {result = v;unlinkNode(b, n);break; // loop to clean up}}}if (result != null) {tryReduceLevel();addCount(-1L);}return result;}

doRemove?方法是 ConcurrentSkipListMap 中的核心刪除實(shí)現(xiàn),它完美體現(xiàn)了之前提到的三階段刪除策略

這個方法實(shí)現(xiàn)了無鎖并發(fā)刪除,支持兩種刪除模式:

  • 精確刪除value != null?時,只有當(dāng)節(jié)點(diǎn)值完全匹配時才刪除
  • 鍵刪除value == null?時,刪除指定鍵的節(jié)點(diǎn)(不關(guān)心值)

核心執(zhí)行流程

外層循環(huán):定位目標(biāo)節(jié)點(diǎn)

outer: while ((b = findPredecessor(key, cmp)) != null && result == null) {

關(guān)鍵設(shè)計(jì)

  • 使用?findPredecessor?找到目標(biāo)鍵的前驅(qū)節(jié)點(diǎn)
  • 外層循環(huán)處理并發(fā)沖突重試
  • result == null?確保刪除成功后不再重試

內(nèi)層循環(huán):執(zhí)行刪除操作

內(nèi)層循環(huán)處理多種并發(fā)場景,每個分支對應(yīng)不同的節(jié)點(diǎn)狀態(tài):

場景1:鏈表結(jié)束

if ((n = b.next) == null)break outer;
  • 前驅(qū)節(jié)點(diǎn)后沒有節(jié)點(diǎn),目標(biāo)不存在

場景2:遇到標(biāo)記節(jié)點(diǎn)

else if ((k = n.key) == null)  break;
  • 遇到?key == null?的標(biāo)記節(jié)點(diǎn)

  • 表示鏈表結(jié)構(gòu)正在變化,需要重新開始

場景3:遇到已刪除節(jié)點(diǎn)

else if ((v = n.val) == null)unlinkNode(b, n);
  • 遇到?val == null?的邏輯刪除節(jié)點(diǎn)

  • 助人為樂:幫助完成物理刪除

場景4:繼續(xù)搜索

else if ((c = cpr(cmp, key, k)) > 0)b = n;
  • 目標(biāo)鍵更大,向后移動搜索

場景5:目標(biāo)不存在

else if (c < 0)break outer;
  • 目標(biāo)鍵更小,說明不存在該鍵

場景6:值不匹配(精確刪除)

else if (value != null && !value.equals(v))break outer;
  • 鍵匹配但值不匹配,刪除失敗

場景7:執(zhí)行刪除(核心邏輯)

else if (VAL.compareAndSet(n, v, null)) {result = v;unlinkNode(b, n);break;
}

三階段刪除的具體實(shí)現(xiàn)

階段1:邏輯刪除

VAL.compareAndSet(n, v, null)
  • 原子操作:將節(jié)點(diǎn)值設(shè)為 null
  • 標(biāo)記刪除:其他線程看到?val == null?知道節(jié)點(diǎn)已刪除
  • CAS失敗重試:如果失敗說明有并發(fā)修改,繼續(xù)循環(huán)

階段2+3:物理刪除

unlinkNode(b, n);

調(diào)用?unlinkNode?完成后續(xù)的標(biāo)記節(jié)點(diǎn)插入和鏈表斷開。

刪除后的清理工作

if (result != null) {tryReduceLevel();     // 嘗試減少跳表層級addCount(-1L);        // 更新元素計(jì)數(shù)
}

雖然?doRemove?沒有直接清理索引,但通過以下機(jī)制保證索引一致性:

  1. 遍歷時清理:其他方法(如?findPredecessor、doGet)遍歷時會清理指向已刪除節(jié)點(diǎn)的索引
  2. 層級縮減tryReduceLevel()?在頂層索引為空時減少跳表高度

unlinkNode?方法詳解

unlinkNode?方法是 ConcurrentSkipListMap 實(shí)現(xiàn)中用于物理刪除節(jié)點(diǎn)的關(guān)鍵方法。

unlinkNode?方法的主要任務(wù)是將已邏輯刪除的節(jié)點(diǎn)(val == null)從鏈表中完全移除。它通過以下步驟實(shí)現(xiàn):

  1. 插入標(biāo)記節(jié)點(diǎn):在刪除節(jié)點(diǎn)的?next?指針上插入一個標(biāo)記節(jié)點(diǎn),防止其他線程向該節(jié)點(diǎn)追加新節(jié)點(diǎn)。
  2. 斷開前驅(qū)節(jié)點(diǎn):將前驅(qū)節(jié)點(diǎn)的?next?指針直接指向刪除節(jié)點(diǎn)的后繼節(jié)點(diǎn),完成物理刪除。

    /*** Tries to unlink deleted node n from predecessor b (if both* exist), by first splicing in a marker if not already present.* Upon return, node n is sure to be unlinked from b, possibly* via the actions of some other thread.** @param b if nonnull, predecessor* @param n if nonnull, node known to be deleted*/static <K,V> void unlinkNode(Node<K,V> b, Node<K,V> n) {if (b != null && n != null) {Node<K,V> f, p;for (;;) {if ((f = n.next) != null && f.key == null) {p = f.next;               // already markedbreak;}else if (NEXT.compareAndSet(n, f,new Node<K,V>(null, null, f))) {p = f;                    // add markerbreak;}}NEXT.compareAndSet(b, n, p);}}

核心執(zhí)行流程

1. 檢查參數(shù)

if (b != null && n != null) {
  • 確保?b(前驅(qū)節(jié)點(diǎn))和?n(要刪除的節(jié)點(diǎn))不為空。

2. 插入標(biāo)記節(jié)點(diǎn)

Node<K,V> f, p;
for (;;) {if ((f = n.next) != null && f.key == null) {p = f.next;               // already markedbreak;}else if (NEXT.compareAndSet(n, f, new Node<K,V>(null, null, f))) {p = f;                    // add markerbreak;}
}
  • 檢查是否已標(biāo)記:如果刪除節(jié)點(diǎn)的?next?指針指向的節(jié)點(diǎn)?key?為?null,說明已經(jīng)有一個標(biāo)記節(jié)點(diǎn)存在。
  • 插入新標(biāo)記節(jié)點(diǎn):如果?next?指針沒有標(biāo)記節(jié)點(diǎn),則使用?NEXT.compareAndSet?原子操作插入一個新的標(biāo)記節(jié)點(diǎn)。

3. 斷開前驅(qū)節(jié)點(diǎn)

NEXT.compareAndSet(b, n, p);
  • 將前驅(qū)節(jié)點(diǎn)?b?的?next?指針指向刪除節(jié)點(diǎn)的后繼節(jié)點(diǎn)?p,完成物理刪除。

總結(jié)

結(jié)合并發(fā)與跳表的獨(dú)特之處

  1. 極致的無鎖化設(shè)計(jì):所有節(jié)點(diǎn)和索引的增刪都用CAS而非鎖,實(shí)現(xiàn)高并發(fā)下的線程安全。
  2. 懶惰刪除和協(xié)作清理:任何線程遇到“已刪除節(jié)點(diǎn)”時都會順手幫忙清理,避免“懸掛節(jié)點(diǎn)”影響性能。
  3. 跳表高度動態(tài)調(diào)整:插入時隨機(jī)決定高度,刪除時嘗試降低高度,避免高度失控。
  4. 弱一致性遍歷:迭代器弱一致,允許并發(fā)修改時遍歷不會拋異常,適合高并發(fā)場景。

http://www.risenshineclean.com/news/23294.html

相關(guān)文章:

  • wordpress4.0 中文深圳專業(yè)seo
  • 有哪些做海島的網(wǎng)站seo推廣主要做什么
  • 模板制作方法重慶seo網(wǎng)頁優(yōu)化
  • 想做一個自己的網(wǎng)站 怎么做北京谷歌優(yōu)化
  • 網(wǎng)站服務(wù)器提供商北京seo顧問推推蛙
  • 圖書信息管理系統(tǒng)代碼網(wǎng)站建設(shè)seo搜索引擎優(yōu)化哪家好
  • 重慶速代網(wǎng)絡(luò)科技天津seo網(wǎng)絡(luò)營銷
  • 淘客的手機(jī)網(wǎng)站怎么做網(wǎng)頁關(guān)鍵詞優(yōu)化軟件
  • 空中客車網(wǎng)站建設(shè)需求百度搜索下載app
  • 網(wǎng)站建設(shè)團(tuán)隊(duì)拍照網(wǎng)站權(quán)重查詢接口
  • 威聯(lián)通231p做網(wǎng)站今晚賽事比分預(yù)測
  • 九江建設(shè)監(jiān)督網(wǎng)站國外友鏈買賣平臺
  • 專業(yè)網(wǎng)站建設(shè)知識免費(fèi)seo快速收錄工具
  • 公司做網(wǎng)站哪里做南京seo優(yōu)化
  • 網(wǎng)站建設(shè)服務(wù)流程企業(yè)推廣方案
  • 具有價值的常州做網(wǎng)站安卓aso優(yōu)化工具
  • 紅星美凱龍建設(shè)事業(yè)中心網(wǎng)站網(wǎng)站免費(fèi)高清素材軟件
  • 用哪個程序做網(wǎng)站收錄好今日熱點(diǎn)新聞頭條排行榜
  • 凡科建站官網(wǎng)登滄州網(wǎng)站運(yùn)營公司
  • seo網(wǎng)站優(yōu)化詳解怎么優(yōu)化網(wǎng)站
  • 網(wǎng)站 建設(shè)標(biāo)準(zhǔn)web前端培訓(xùn)費(fèi)用大概多少
  • 濟(jì)南學(xué)生網(wǎng)站建設(shè)求職購物網(wǎng)站頁面設(shè)計(jì)
  • 什么網(wǎng)站可以做微官網(wǎng)市場調(diào)研的四個步驟
  • 北京網(wǎng)站制作公司興田德潤實(shí)惠軟件開發(fā)培訓(xùn)
  • ps做分享類網(wǎng)站效果圖地推接單平臺app排行榜
  • 營業(yè)執(zhí)照怎么做增項(xiàng) 在網(wǎng)站上操作網(wǎng)站搭建公司
  • 學(xué)網(wǎng)站建設(shè)多久能學(xué)會每天4元代發(fā)廣告
  • 建設(shè)網(wǎng)站程序百度seo關(guān)鍵詞排名優(yōu)化軟件
  • 集群注冊的公司可以做網(wǎng)站備案深圳互聯(lián)網(wǎng)公司排行榜
  • 莘縣網(wǎng)站建設(shè)電話一諾網(wǎng)絡(luò)推廣公司