小红书Java面试被问:使用VarHandle实现内存安全的无锁数据结构
一、VarHandle 基础与优势
1. VarHandle 与 Atomic 对比
java
复制
下载
// 传统的 Atomic 类使用 AtomicInteger count = new AtomicInteger(0); count.compareAndSet(0, 1); // CAS 操作 // VarHandle 实现相同功能 class Counter { private volatile int value = 0; private static final VarHandle VALUE_HANDLE; static { try { VALUE_HANDLE = MethodHandles.lookup() .findVarHandle(Counter.class, "value", int.class); } catch (Exception e) { throw new Error(e); } } public boolean compareAndSet(int expected, int update) { return VALUE_HANDLE.compareAndSet(this, expected, update); } }
2. VarHandle 的核心特性
java
复制
下载
public class VarHandleFeatures { /** * VarHandle 相比 Unsafe 的优势: * 1. 类型安全 - 编译期检查 * 2. 内存语义明确 - acquire/release/volatile * 3. 平台无关性 - 在 AOT 编译中有更好支持 * 4. 访问模式 - 支持多种访问模式 */ // VarHandle 支持的内存访问模式 enum AccessMode { GET, // 普通读 SET, // 普通写 GET_VOLATILE, // volatile 读 SET_VOLATILE, // volatile 写 GET_ACQUIRE, // acquire 语义读 SET_RELEASE, // release 语义写 GET_OPAQUE, // opaque 读(无内存排序保证) SET_OPAQUE, // opaque 写 COMPARE_AND_SET, // CAS COMPARE_AND_EXCHANGE, // 比较并交换 GET_AND_SET, // 获取并设置 GET_AND_ADD, // 获取并增加 GET_AND_BITWISE_OR // 获取并按位或 } }
二、无锁栈实现(Treiber Stack)
1. 完整的无锁栈实现
java
复制
下载
import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.util.NoSuchElementException; import java.util.concurrent.atomic.AtomicReference; /** * 基于 VarHandle 实现的内存安全无锁栈 * Treiber Stack 算法,使用 CAS 实现线程安全 */ public class LockFreeStack<E> { /** * 栈节点定义 */ private static class Node<E> { final E item; Node<E> next; Node(E item) { this.item = item; this.next = null; } } /** * 使用 VarHandle 替代 AtomicReference */ private volatile Node<E> top = null; private static final VarHandle TOP_HANDLE; static { try { TOP_HANDLE = MethodHandles.lookup() .findVarHandle(LockFreeStack.class, "top", Node.class); } catch (Exception e) { throw new Error(e); } } /** * 压栈操作 - 无锁实现 * @param item 要压入的元素 */ public void push(E item) { Node<E> newNode = new Node<>(item); Node<E> currentTop; do { // acquire 语义读当前栈顶 currentTop = (Node<E>) TOP_HANDLE.getAcquire(this); newNode.next = currentTop; // CAS 操作更新栈顶 // release 语义确保 newNode 的初始化对后续读可见 } while (!TOP_HANDLE.compareAndSet(this, currentTop, newNode)); // 可选:压栈后的清理或通知操作 postPush(newNode); } /** * 弹栈操作 - 无锁实现 * @return 弹出的元素 * @throws NoSuchElementException 如果栈为空 */ public E pop() { Node<E> currentTop; Node<E> nextTop; do { // volatile 读当前栈顶 currentTop = (Node<E>) TOP_HANDLE.getVolatile(this); if (currentTop == null) { throw new NoSuchElementException("Stack is empty"); } nextTop = currentTop.next; // CAS 操作更新栈顶 // 使用 release 语义确保弹栈操作的可见性 } while (!TOP_HANDLE.compareAndSetRelease(this, currentTop, nextTop)); // 帮助 GC,断开引用 currentTop.next = null; return currentTop.item; } /** * 安全的弹栈操作(无异常版) * @return 弹出的元素,如果栈为空则返回 null */ public E popSafe() { Node<E> currentTop; Node<E> nextTop; do { currentTop = (Node<E>) TOP_HANDLE.getVolatile(this); if (currentTop == null) { return null; // 栈为空 } nextTop = currentTop.next; // 使用 weakCompareAndSet 在某些场景下性能更好 } while (!TOP_HANDLE.weakCompareAndSet(this, currentTop, nextTop)); E item = currentTop.item; currentTop.next = null; // 帮助 GC return item; } /** * 批量压栈操作 - 减少 CAS 竞争 * @param items 要压入的元素数组 */ public void pushAll(E[] items) { if (items == null || items.length == 0) { return; } // 构建本地链表 Node<E> first = new Node<>(items[0]); Node<E> last = first; for (int i = 1; i < items.length; i++) { Node<E> node = new Node<>(items[i]); last.next = node; last = node; } Node<E> currentTop; do { // 获取当前栈顶 currentTop = (Node<E>) TOP_HANDLE.getAcquire(this); last.next = currentTop; // 一次性将整个链表压入 } while (!TOP_HANDLE.compareAndSet(this, currentTop, first)); } /** * 查看栈顶元素(不弹出) * @return 栈顶元素,如果栈为空则返回 null */ public E peek() { // acquire 语义读,确保看到最新的数据 Node<E> currentTop = (Node<E>) TOP_HANDLE.getAcquire(this); return currentTop != null ? currentTop.item : null; } /** * 栈是否为空 * @return 如果栈为空返回 true */ public boolean isEmpty() { // 使用 opaque 读,减少内存屏障开销 return TOP_HANDLE.getOpaque(this) == null; } /** * 获取栈的大小(非线程安全,仅用于调试) * @return 栈的近似大小 */ public int size() { int count = 0; // acquire 语义确保读到最新的链表 Node<E> current = (Node<E>) TOP_HANDLE.getAcquire(this); while (current != null) { count++; current = current.next; } return count; } /** * 清空栈 */ public void clear() { // 使用 setVolatile 确保清空操作对其他线程可见 TOP_HANDLE.setVolatile(this, null); } /** * 压栈后的回调(模板方法,子类可重写) */ protected void postPush(Node<E> newNode) { // 默认空实现,可用于统计、监控等 } /** * 性能优化的弹栈操作 - 使用自旋优化 */ public E popOptimized() { final int MAX_SPINS = 100; // 最大自旋次数 int spins = 0; while (true) { Node<E> currentTop = (Node<E>) TOP_HANDLE.getAcquire(this); if (currentTop == null) { return null; } // 检查是否有可能竞争 if (spins < MAX_SPINS && TOP_HANDLE.getVolatile(this) == currentTop) { spins++; Thread.onSpinWait(); // JDK 9+ 自旋提示 continue; } Node<E> nextTop = currentTop.next; if (TOP_HANDLE.compareAndSet(this, currentTop, nextTop)) { E item = currentTop.item; currentTop.next = null; return item; } spins = 0; // CAS 失败,重置自旋计数 } } }
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
2. 带 ABA 问题防护的增强版栈
java
复制
下载
/** * 使用指针标记解决 ABA 问题的无锁栈 * 通过版本号防止 ABA 问题 */ public class LockFreeStackWithStamped<E> { /** * 带标记的指针 */ private static class StampedReference<T> { final T reference; final long stamp; // 版本号 StampedReference(T reference, long stamp) { this.reference = reference; this.stamp = stamp; } } /** * 栈节点 */ private static class Node<E> { final E item; volatile Node<E> next; Node(E item) { this.item = item; } } /** * 使用 VarHandle 管理带标记的栈顶 */ private volatile StampedReference<Node<E>> top = new StampedReference<>(null, 0); private static final VarHandle TOP_HANDLE; private static final VarHandle STAMP_HANDLE; static { try { TOP_HANDLE = MethodHandles.lookup() .findVarHandle( LockFreeStackWithStamped.class, "top", StampedReference.class ); } catch (Exception e) { throw new Error(e); } } /** * 压栈 - 带版本号的 CAS */ public void push(E item) { Node<E> newNode = new Node<>(item); StampedReference<Node<E>> currentTop; StampedReference<Node<E>> newTop; do { // 获取当前栈顶和版本号 currentTop = (StampedReference<Node<E>>) TOP_HANDLE.getAcquire(this); newNode.next = currentTop.reference; // 创建新的带版本号的引用 newTop = new StampedReference<>( newNode, currentTop.stamp + 1 // 版本号递增 ); } while (!TOP_HANDLE.compareAndSet( this, currentTop, newTop )); } /** * 弹栈 - 带版本号的 CAS */ public E pop() { StampedReference<Node<E>> currentTop; StampedReference<Node<E>> newTop; Node<E> nextNode; do { currentTop = (StampedReference<Node<E>>) TOP_HANDLE.getVolatile(this); Node<E> topNode = currentTop.reference; if (topNode == null) { return null; } nextNode = topNode.next; // 版本号递增 newTop = new StampedReference<>( nextNode, currentTop.stamp + 1 ); } while (!TOP_HANDLE.compareAndSetRelease( this, currentTop, newTop )); return currentTop.reference.item; } }
三、无锁队列实现(Michael-Scott 队列)
1. 完整的无锁队列实现
java
复制
下载
import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.util.NoSuchElementException; import java.util.concurrent.atomic.AtomicReference; /** * 基于 VarHandle 实现的内存安全无锁队列 * Michael-Scott 算法,支持多生产者多消费者 */ public class LockFreeQueue<E> { /** * 队列节点 */ private static class Node<E> { volatile E item; volatile Node<E> next; Node(E item) { this.item = item; } } /** * 哑节点(sentinel) */ private volatile Node<E> dummy = new Node<>(null); /** * 队列头指针(始终指向哑节点) */ private volatile Node<E> head; /** * 队列尾指针 */ private volatile Node<E> tail; /** * VarHandle 声明 */ private static final VarHandle HEAD_HANDLE; private static final VarHandle TAIL_HANDLE; private static final VarHandle NEXT_HANDLE; private static final VarHandle ITEM_HANDLE; static { try { MethodHandles.Lookup lookup = MethodHandles.lookup(); HEAD_HANDLE = lookup.findVarHandle( LockFreeQueue.class, "head", Node.class ); TAIL_HANDLE = lookup.findVarHandle( LockFreeQueue.class, "tail", Node.class ); NEXT_HANDLE = lookup.findVarHandle( Node.class, "next", Node.class ); ITEM_HANDLE = lookup.findVarHandle( Node.class, "item", Object.class ); } catch (Exception e) { throw new Error(e); } } /** * 构造函数 */ public LockFreeQueue() { head = dummy; tail = dummy; } /** * 入队操作 */ public void enqueue(E item) { if (item == null) { throw new NullPointerException(); } Node<E> newNode = new Node<>(item); Node<E> currentTail; Node<E> tailNext; while (true) { // 获取当前尾节点 currentTail = (Node<E>) TAIL_HANDLE.getAcquire(this); tailNext = (Node<E>) NEXT_HANDLE.getAcquire(currentTail); // 检查尾节点是否仍然有效 if (currentTail == (Node<E>) TAIL_HANDLE.getVolatile(this)) { if (tailNext == null) { // 尝试链接新节点 if (NEXT_HANDLE.compareAndSet( currentTail, null, newNode)) { // 成功链接,尝试更新尾指针 TAIL_HANDLE.compareAndSetRelease( this, currentTail, newNode ); return; } } else { // 帮助其他线程推进尾指针 TAIL_HANDLE.compareAndSet( this, currentTail, tailNext ); } } } } /** * 出队操作 */ public E dequeue() { while (true) { Node<E> currentHead = (Node<E>) HEAD_HANDLE.getAcquire(this); Node<E> currentTail = (Node<E>) TAIL_HANDLE.getAcquire(this); Node<E> headNext = (Node<E>) NEXT_HANDLE.getAcquire(currentHead); // 检查队列状态 if (currentHead == (Node<E>) HEAD_HANDLE.getVolatile(this)) { if (currentHead == currentTail) { if (headNext == null) { throw new NoSuchElementException("Queue is empty"); } // 尾指针落后,帮助推进 TAIL_HANDLE.compareAndSet( this, currentTail, headNext ); } else { // 读取要返回的元素 E item = (E) ITEM_HANDLE.getVolatile(headNext); if (item != null) { // 尝试出队 if (HEAD_HANDLE.compareAndSetRelease( this, currentHead, headNext)) { // 清空旧头节点的引用,帮助 GC ITEM_HANDLE.setVolatile(currentHead, null); NEXT_HANDLE.setVolatile(currentHead, null); return item; } } } } } } /** * 安全的出队操作 */ public E dequeueSafe() { while (true) { Node<E> currentHead = (Node<E>) HEAD_HANDLE.getAcquire(this); Node<E> currentTail = (Node<E>) TAIL_HANDLE.getAcquire(this); Node<E> headNext = (Node<E>) NEXT_HANDLE.getAcquire(currentHead); if (currentHead == (Node<E>) HEAD_HANDLE.getVolatile(this)) { if (currentHead == currentTail) { if (headNext == null) { return null; // 队列为空 } // 帮助推进尾指针 TAIL_HANDLE.compareAndSet( this, currentTail, headNext ); } else { E item = (E) ITEM_HANDLE.getVolatile(headNext); if (item != null && HEAD_HANDLE.compareAndSet( this, currentHead, headNext)) { // 清理 ITEM_HANDLE.setRelease(currentHead, null); NEXT_HANDLE.setRelease(currentHead, null); return item; } } } } } /** * 查看队首元素 */ public E peek() { while (true) { Node<E> currentHead = (Node<E>) HEAD_HANDLE.getAcquire(this); Node<E> headNext = (Node<E>) NEXT_HANDLE.getAcquire(currentHead); if (currentHead == (Node<E>) HEAD_HANDLE.getVolatile(this)) { if (headNext == null) { return null; } E item = (E) ITEM_HANDLE.getAcquire(headNext); if (item != null) { return item; } } } } /** * 队列是否为空 */ public boolean isEmpty() { Node<E> currentHead = (Node<E>) HEAD_HANDLE.getOpaque(this); Node<E> currentTail = (Node<E>) TAIL_HANDLE.getOpaque(this); return currentHead == currentTail && NEXT_HANDLE.getOpaque(currentHead) == null; } /** * 批量入队 */ public void enqueueAll(E[] items) { if (items == null || items.length == 0) { return; } // 构建本地链表 Node<E> first = new Node<>(items[0]); Node<E> last = first; for (int i = 1; i < items.length; i++) { Node<E> node = new Node<>(items[i]); NEXT_HANDLE.setRelease(last, node); last = node; } Node<E> currentTail; Node<E> tailNext; while (true) { currentTail = (Node<E>) TAIL_HANDLE.getAcquire(this); tailNext = (Node<E>) NEXT_HANDLE.getAcquire(currentTail); if (currentTail == (Node<E>) TAIL_HANDLE.getVolatile(this)) { if (tailNext == null) { if (NEXT_HANDLE.compareAndSet( currentTail, null, first)) { // 更新尾指针 TAIL_HANDLE.compareAndSetRelease( this, currentTail, last ); return; } } else { // 帮助推进尾指针 TAIL_HANDLE.compareAndSet( this, currentTail, tailNext ); } } } } }
2. 无锁双端队列
java
复制
下载
/** * 无锁双端队列 * 基于 CAS 实现的双向链表 */ public class LockFreeDeque<E> { private static class Node<E> { volatile E item; volatile Node<E> prev; volatile Node<E> next; Node(E item) { this.item = item; } } /** * 哑节点 */ private final Node<E> sentinel = new Node<>(null); /** * 队首指针(指向第一个真实节点) */ private volatile Node<E> head = sentinel; /** * 队尾指针(指向最后一个真实节点) */ private volatile Node<E> tail = sentinel; /** * VarHandle 声明 */ private static final VarHandle HEAD_HANDLE; private static final VarHandle TAIL_HANDLE; private static final VarHandle NEXT_HANDLE; private static final VarHandle PREV_HANDLE; private static final VarHandle ITEM_HANDLE; static { try { MethodHandles.Lookup lookup = MethodHandles.lookup(); HEAD_HANDLE = lookup.findVarHandle( LockFreeDeque.class, "head", Node.class ); TAIL_HANDLE = lookup.findVarHandle( LockFreeDeque.class, "tail", Node.class ); NEXT_HANDLE = lookup.findVarHandle( Node.class, "next", Node.class ); PREV_HANDLE = lookup.findVarHandle( Node.class, "prev", Node.class ); ITEM_HANDLE = lookup.findVarHandle( Node.class, "item", Object.class ); } catch (Exception e) { throw new Error(e); } } public LockFreeDeque() { // 初始化双向链表 NEXT_HANDLE.setRelease(sentinel, sentinel); PREV_HANDLE.setRelease(sentinel, sentinel); } /** * 从头部添加 */ public void addFirst(E item) { if (item == null) throw new NullPointerException(); Node<E> newNode = new Node<>(item); Node<E> currentHead; do { currentHead = (Node<E>) HEAD_HANDLE.getAcquire(this); Node<E> next = (Node<E>) NEXT_HANDLE.getAcquire(currentHead); // 设置新节点的前后指针 NEXT_HANDLE.setRelease(newNode, next); PREV_HANDLE.setRelease(newNode, currentHead); // 更新原头节点的后指针 } while (!NEXT_HANDLE.compareAndSet( currentHead, next, newNode )); // 更新新头节点 HEAD_HANDLE.compareAndSetRelease(this, currentHead, newNode); } /** * 从尾部添加 */ public void addLast(E item) { if (item == null) throw new NullPointerException(); Node<E> newNode = new Node<>(item); Node<E> currentTail; do { currentTail = (Node<E>) TAIL_HANDLE.getAcquire(this); Node<E> prev = (Node<E>) PREV_HANDLE.getAcquire(currentTail); // 设置新节点的前后指针 PREV_HANDLE.setRelease(newNode, prev); NEXT_HANDLE.setRelease(newNode, currentTail); // 更新原尾节点的前指针 } while (!PREV_HANDLE.compareAndSet( currentTail, prev, newNode )); // 更新新尾节点 TAIL_HANDLE.compareAndSetRelease(this, currentTail, newNode); } /** * 移除头部元素 */ public E removeFirst() { Node<E> currentHead; Node<E> nextNode; do { currentHead = (Node<E>) HEAD_HANDLE.getAcquire(this); nextNode = (Node<E>) NEXT_HANDLE.getAcquire(currentHead); if (nextNode == sentinel) { return null; // 队列为空 } Node<E> nextNext = (Node<E>) NEXT_HANDLE.getAcquire(nextNode); // 跳过要移除的节点 } while (!NEXT_HANDLE.compareAndSet( currentHead, nextNode, nextNext )); // 更新头部 HEAD_HANDLE.compareAndSetRelease(this, currentHead, nextNode); // 清理移除的节点 E item = (E) ITEM_HANDLE.getAndSet(nextNode, null); NEXT_HANDLE.setRelease(nextNode, null); PREV_HANDLE.setRelease(nextNode, null); return item; } /** * 移除尾部元素 */ public E removeLast() { Node<E> currentTail; Node<E> prevNode; do { currentTail = (Node<E>) TAIL_HANDLE.getAcquire(this); prevNode = (Node<E>) PREV_HANDLE.getAcquire(currentTail); if (prevNode == sentinel) { return null; // 队列为空 } Node<E> prevPrev = (Node<E>) PREV_HANDLE.getAcquire(prevNode); // 跳过要移除的节点 } while (!PREV_HANDLE.compareAndSet( currentTail, prevNode, prevPrev )); // 更新尾部 TAIL_HANDLE.compareAndSetRelease(this, currentTail, prevNode); // 清理移除的节点 E item = (E) ITEM_HANDLE.getAndSet(prevNode, null); NEXT_HANDLE.setRelease(prevNode, null); PREV_HANDLE.setRelease(prevNode, null); return item; } }
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
四、无锁哈希表实现
1. 无锁哈希表基础实现
java
复制
下载
import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.util.function.Function; /** * 无锁哈希表实现 * 使用开放寻址法和 CAS 操作 */ public class LockFreeHashTable<K, V> { /** * 哈希表条目 */ private static class Entry<K, V> { final K key; volatile V value; volatile int hash; // key 的哈希值缓存 Entry(K key, V value, int hash) { this.key = key; this.value = value; this.hash = hash; } } /** * 哈希表桶数组 */ private volatile Entry<K, V>[] table; /** * 大小(元素个数) */ private volatile int size = 0; /** * 容量阈值(触发扩容) */ private volatile int threshold; /** * VarHandle 声明 */ private static final VarHandle TABLE_HANDLE; private static final VarHandle SIZE_HANDLE; private static final VarHandle ENTRY_VALUE_HANDLE; private static final VarHandle ENTRY_HASH_HANDLE; static { try { MethodHandles.Lookup lookup = MethodHandles.lookup(); TABLE_HANDLE = lookup.findVarHandle( LockFreeHashTable.class, "table", Entry[].class ); SIZE_HANDLE = lookup.findVarHandle( LockFreeHashTable.class, "size", int.class ); ENTRY_VALUE_HANDLE = lookup.findVarHandle( Entry.class, "value", Object.class ); ENTRY_HASH_HANDLE = lookup.findVarHandle( Entry.class, "hash", int.class ); } catch (Exception e) { throw new Error(e); } } /** * 默认初始容量 */ private static final int DEFAULT_INITIAL_CAPACITY = 16; /** * 负载因子 */ private static final float LOAD_FACTOR = 0.75f; /** * 空键标记(用于标记删除的条目) */ private static final Object TOMBSTONE = new Object(); @SuppressWarnings("unchecked") public LockFreeHashTable() { table = (Entry<K, V>[]) new Entry<?, ?>[DEFAULT_INITIAL_CAPACITY]; threshold = (int) (DEFAULT_INITIAL_CAPACITY * LOAD_FACTOR); } /** * 获取值 */ public V get(K key) { int hash = hash(key); Entry<K, V>[] tab = (Entry<K, V>[]) TABLE_HANDLE.getAcquire(this); int index = indexFor(hash, tab.length); // 线性探测 for (int i = 0; i < tab.length; i++) { Entry<K, V> entry = tabAt(tab, index); if (entry == null) { return null; // 未找到 } if (entry.key == TOMBSTONE) { // 跳过已删除的条目 index = nextIndex(index, tab.length); continue; } // acquire 语义读哈希值 int entryHash = (int) ENTRY_HASH_HANDLE.getAcquire(entry); if (entryHash == hash && (entry.key == key || (key != null && key.equals(entry.key)))) { // acquire 语义读值 return (V) ENTRY_VALUE_HANDLE.getAcquire(entry); } index = nextIndex(index, tab.length); } return null; } /** * 插入或更新值 */ public V put(K key, V value) { if (key == null) throw new NullPointerException(); int hash = hash(key); Entry<K, V>[] tab; int index; while (true) { tab = (Entry<K, V>[]) TABLE_HANDLE.getAcquire(this); index = indexFor(hash, tab.length); // 查找现有条目或空位 for (int i = 0; i < tab.length; i++) { Entry<K, V> entry = tabAt(tab, index); if (entry == null) { // 找到空位,尝试插入 if (tryPutNewEntry(tab, index, key, value, hash)) { return null; } // CAS 失败,重试 break; } if (entry.key == TOMBSTONE) { // 尝试复用已删除的位置 if (tryReplaceTombstone(tab, index, key, value, hash)) { return null; } // 失败则继续探测 } // 检查是否为相同 key int entryHash = (int) ENTRY_HASH_HANDLE.getAcquire(entry); if (entryHash == hash && (entry.key == key || key.equals(entry.key))) { // 更新现有值 V oldValue = (V) ENTRY_VALUE_HANDLE.getAcquire(entry); if (ENTRY_VALUE_HANDLE.compareAndSet( entry, oldValue, value)) { return oldValue; } // CAS 失败,重试 break; } index = nextIndex(index, tab.length); } // 检查是否需要扩容 if (needResize()) { resize(); } } } /** * 尝试插入新条目 */ private boolean tryPutNewEntry(Entry<K, V>[] tab, int index, K key, V value, int hash) { Entry<K, V> newEntry = new Entry<>(key, value, hash); // 使用 VarHandle 的 compareAndExchange Entry<K, V> expected = null; Entry<K, V> actual = (Entry<K, V>) TABLE_HANDLE.compareAndExchange( this, tab, tab // 检查表未改变 ); if (actual != tab) { return false; // 表已改变,需要重试 } // CAS 设置数组元素 VarHandle arrayHandle = MethodHandles.arrayElementVarHandle(Entry[].class); if (arrayHandle.compareAndSet(tab, index, null, newEntry)) { // 成功插入,增加大小 incrementSize(); return true; } return false; } /** * 原子增加大小 */ private void incrementSize() { int current; do { current = (int) SIZE_HANDLE.getVolatile(this); } while (!SIZE_HANDLE.compareAndSet(this, current, current + 1)); } /** * 删除键值对 */ public V remove(K key) { int hash = hash(key); Entry<K, V>[] tab; int index; while (true) { tab = (Entry<K, V>[]) TABLE_HANDLE.getAcquire(this); index = indexFor(hash, tab.length); for (int i = 0; i < tab.length; i++) { Entry<K, V> entry = tabAt(tab, index); if (entry == null) { return null; // 未找到 } if (entry.key == TOMBSTONE) { index = nextIndex(index, tab.length); continue; } int entryHash = (int) ENTRY_HASH_HANDLE.getAcquire(entry); if (entryHash == hash && (entry.key == key || key.equals(entry.key))) { // 获取当前值 V oldValue = (V) ENTRY_VALUE_HANDLE.getAcquire(entry); // 标记为已删除(tombstone) if (ENTRY_VALUE_HANDLE.compareAndSet( entry, oldValue, TOMBSTONE)) { // 减少大小 decrementSize(); return oldValue; } // CAS 失败,重试 break; } index = nextIndex(index, tab.length); } } } /** * 原子减少大小 */ private void decrementSize() { int current; do { current = (int) SIZE_HANDLE.getVolatile(this); } while (!SIZE_HANDLE.compareAndSet(this, current, current - 1)); } /** * 计算索引 */ private int indexFor(int hash, int length) { return hash & (length - 1); // 假设 length 是 2 的幂 } /** * 下一个索引(线性探测) */ private int nextIndex(int index, int length) { return (index + 1) & (length - 1); } /** * 哈希函数 */ private int hash(Object key) { int h = key.hashCode(); // 扰动函数,减少碰撞 h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } /** * 获取数组元素 */ @SuppressWarnings("unchecked") private Entry<K, V> tabAt(Entry<K, V>[] tab, int index) { VarHandle arrayHandle = MethodHandles.arrayElementVarHandle(Entry[].class); return (Entry<K, V>) arrayHandle.getVolatile(tab, index); } /** * 检查是否需要扩容 */ private boolean needResize() { int currentSize = (int) SIZE_HANDLE.getOpaque(this); return currentSize >= threshold; } /** * 扩容操作 */ @SuppressWarnings("unchecked") private void resize() { Entry<K, V>[] oldTab = (Entry<K, V>[]) TABLE_HANDLE.getAcquire(this); int oldCap = oldTab.length; if (oldCap >= (1 << 30)) { threshold = Integer.MAX_VALUE; // 不能再扩容 return; } int newCap = oldCap << 1; // 双倍扩容 Entry<K, V>[] newTab = (Entry<K, V>[]) new Entry<?, ?>[newCap]; // 尝试 CAS 设置新表 if (TABLE_HANDLE.compareAndSet(this, oldTab, newTab)) { // 重新哈希所有元素 rehash(oldTab, newTab); threshold = (int) (newCap * LOAD_FACTOR); } } /** * 重新哈希 */ private void rehash(Entry<K, V>[] oldTab, Entry<K, V>[] newTab) { for (Entry<K, V> entry : oldTab) { if (entry != null && entry.key != TOMBSTONE) { int newIndex = indexFor(entry.hash, newTab.length); // 插入到新表 while (true) { Entry<K, V> existing = tabAt(newTab, newIndex); if (existing == null) { VarHandle arrayHandle = MethodHandles.arrayElementVarHandle(Entry[].class); if (arrayHandle.compareAndSet( newTab, newIndex, null, entry)) { break; } } newIndex = nextIndex(newIndex, newTab.length); } } } } /** * 计算大小(近似值) */ public int size() { return (int) SIZE_HANDLE.getAcquire(this); } /** * 使用 computeIfAbsent 模式 */ public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) { V value = get(key); if (value != null) { return value; } V newValue = mappingFunction.apply(key); if (newValue == null) { return null; } V result = put(key, newValue); return result != null ? result : newValue; } }
五、性能优化技巧
1. 内存布局优化
java
复制
下载
/** * 缓存行填充,避免伪共享 * 64字节对齐,针对现代CPU缓存行大小 */ public class PaddedAtomicReference<T> { // 前填充 private volatile long p1, p2, p3, p4, p5, p6, p7; // 核心数据 private volatile T value; // 后填充 private volatile long p8, p9, p10, p11, p12, p13, p14, p15; private static final VarHandle VALUE_HANDLE; static { try { VALUE_HANDLE = MethodHandles.lookup() .findVarHandle(PaddedAtomicReference.class, "value", Object.class); } catch (Exception e) { throw new Error(e); } } public boolean compareAndSet(T expected, T update) { return VALUE_HANDLE.compareAndSet(this, expected, update); } @SuppressWarnings("unchecked") public T get() { return (T) VALUE_HANDLE.getAcquire(this); } public void set(T newValue) { VALUE_HANDLE.setRelease(this, newValue); } } /** * 使用 @Contended 注解(JDK 8+) */ import jdk.internal.vm.annotation.Contended; public class ContendedCounter { @Contended // 防止伪共享 private volatile long value1 = 0; @Contended private volatile long value2 = 0; private static final VarHandle VALUE1_HANDLE; private static final VarHandle VALUE2_HANDLE; static { try { VALUE1_HANDLE = MethodHandles.lookup() .findVarHandle(ContendedCounter.class, "value1", long.class); VALUE2_HANDLE = MethodHandles.lookup() .findVarHandle(ContendedCounter.class, "value2", long.class); } catch (Exception e) { throw new Error(e); } } public void increment1() { long current; do { current = (long) VALUE1_HANDLE.getAcquire(this); } while (!VALUE1_HANDLE.compareAndSet(this, current, current + 1)); } }
2. 批量操作优化
java
复制
下载
/** * 批量计数器,减少 CAS 竞争 */ public class BatchCounter { private static final int BATCH_SIZE = 64; // 线程本地计数器 private static final ThreadLocal<ThreadLocalCounter> COUNTERS = ThreadLocal.withInitial(ThreadLocalCounter::new); // 全局计数器 private volatile long globalCount = 0; private static final VarHandle GLOBAL_COUNT_HANDLE; static { try { GLOBAL_COUNT_HANDLE = MethodHandles.lookup() .findVarHandle(BatchCounter.class, "globalCount", long.class); } catch (Exception e) { throw new Error(e); } } /** * 线程本地计数器 */ private static class ThreadLocalCounter { private long localCount = 0; private int batch = 0; public void increment() { localCount++; batch++; // 达到批量大小时,同步到全局 if (batch >= BATCH_SIZE) { flushToGlobal(); } } private void flushToGlobal() { if (localCount > 0) { long currentGlobal; do { currentGlobal = (long) GLOBAL_COUNT_HANDLE.getAcquire(BatchCounter.this); } while (!GLOBAL_COUNT_HANDLE.compareAndSet( BatchCounter.this, currentGlobal, currentGlobal + localCount )); localCount = 0; batch = 0; } } } public void increment() { COUNTERS.get().increment(); } public long get() { // 获取全局计数前,先同步所有线程本地计数 COUNTERS.get().flushToGlobal(); // 返回全局计数 return (long) GLOBAL_COUNT_HANDLE.getAcquire(this); } }
六、测试与验证
1. 并发测试框架
java
复制
下载
import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; public class LockFreeStructureTest { /** * 测试无锁栈 */ public static void testLockFreeStack() throws Exception { LockFreeStack<Integer> stack = new LockFreeStack<>(); int threadCount = 10; int operationsPerThread = 10000; ExecutorService executor = Executors.newFixedThreadPool(threadCount); CyclicBarrier barrier = new CyclicBarrier(threadCount); AtomicInteger pushCount = new AtomicInteger(0); AtomicInteger popCount = new AtomicInteger(0); // 创建测试任务 Runnable task = () -> { try { barrier.await(); // 同时开始 for (int i = 0; i < operationsPerThread; i++) { // 压栈 stack.push(i); pushCount.incrementAndGet(); // 偶尔弹栈 if (i % 3 == 0) { stack.popSafe(); popCount.incrementAndGet(); } } } catch (Exception e) { e.printStackTrace(); } }; // 提交任务 for (int i = 0; i < threadCount; i++) { executor.submit(task); } executor.shutdown(); executor.awaitTermination(1, TimeUnit.MINUTES); // 验证 System.out.println("Push operations: " + pushCount.get()); System.out.println("Pop operations: " + popCount.get()); System.out.println("Final stack size: " + stack.size()); // 清空栈,验证无内存泄漏 Integer item; int remaining = 0; while ((item = stack.popSafe()) != null) { remaining++; } System.out.println("Remaining items after cleanup: " + remaining); assert remaining == pushCount.get() - popCount.get(); } /** * 测试 ABA 防护 */ public static void testABAPrevention() throws Exception { LockFreeStackWithStamped<Integer> stack = new LockFreeStackWithStamped<>(); // 模拟 ABA 场景 stack.push(1); stack.push(2); // 线程1:读取栈顶为2 // 线程2:弹出2,弹出1,压入2(相同值但不同对象) // 线程1:尝试CAS,应该失败(因为有版本号) ExecutorService executor = Executors.newFixedThreadPool(2); Future<?> future1 = executor.submit(() -> { // 线程1:记录栈顶,休眠 Integer top = stack.peek(); try { Thread.sleep(100); // 让线程2有时间操作 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } // 尝试弹栈(应该失败或得到新对象) return stack.pop(); }); Future<?> future2 = executor.submit(() -> { // 线程2:制造ABA场景 stack.pop(); // 弹出2 stack.pop(); // 弹出1 stack.push(2); // 压入新2(不同Node对象) return null; }); future2.get(); Object result = future1.get(); System.out.println("ABA test result: " + result); // 验证结果应该是新压入的2,而不是原来的2 executor.shutdown(); } /** * 性能对比测试 */ public static void performanceComparison() { int iterations = 1000000; // 测试 ConcurrentLinkedQueue Queue<Integer> clq = new ConcurrentLinkedQueue<>(); long start = System.nanoTime(); for (int i = 0; i < iterations; i++) { clq.offer(i); if (i % 2 == 0) { clq.poll(); } } long clqTime = System.nanoTime() - start; // 测试我们的无锁队列 LockFreeQueue<Integer> lfq = new LockFreeQueue<>(); start = System.nanoTime(); for (int i = 0; i < iterations; i++) { lfq.enqueue(i); if (i % 2 == 0) { lfq.dequeueSafe(); } } long lfqTime = System.nanoTime() - start; System.out.println("ConcurrentLinkedQueue time: " + clqTime / 1e6 + " ms"); System.out.println("LockFreeQueue time: " + lfqTime / 1e6 + " ms"); System.out.println("Speedup: " + (double) clqTime / lfqTime); } public static void main(String[] args) throws Exception { System.out.println("Testing LockFreeStack..."); testLockFreeStack(); System.out.println("\nTesting ABA Prevention..."); testABAPrevention(); System.out.println("\nPerformance Comparison..."); performanceComparison(); } }
七、最佳实践与注意事项
1. 内存语义选择指南
java
复制
下载
public class MemorySemanticsGuide { /** * VarHandle 内存语义选择: * * 1. getAcquire / setRelease * - 用于建立 happens-before 关系 * - 适用于发布-订阅模式 * * 2. getVolatile / setVolatile * - 完全的 volatile 语义 * - 确保所有线程的可见性 * * 3. getOpaque / setOpaque * - 仅保证原子性,不保证内存顺序 * - 性能最好,适用于统计计数等场景 * * 4. compareAndSet * - 具有 volatile 语义的 CAS * - 适用于无锁算法 * * 5. weakCompareAndSet * - 可能更高效,但可能假失败 * - 适用于高竞争场景 */ // 示例:正确的内存语义使用 class CorrectUsage { private volatile Data data; private static final VarHandle DATA_HANDLE; void publisher() { Data newData = new Data(); // 初始化数据 newData.init(); // release 语义发布 DATA_HANDLE.setRelease(this, newData); } void consumer() { // acquire 语义获取 Data currentData = (Data) DATA_HANDLE.getAcquire(this); if (currentData != null) { // 可以安全地读取数据内容 currentData.process(); } } } }
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
2. 常见陷阱与解决方法
java
复制
下载
public class CommonPitfalls { /** * 陷阱1:忘记 volatile 修饰符 * 解决:必须用 volatile 修饰 VarHandle 操作的字段 */ class Pitfall1 { private int value; // 错误:缺少 volatile // VarHandle 操作仍能工作,但内存语义不正确 } /** * 陷阱2:错误的初始化顺序 * 解决:使用静态初始化块,确保线程安全初始化 */ class Pitfall2 { // 正确做法 private static final VarHandle HANDLE; static { try { HANDLE = MethodHandles.lookup() .findVarHandle(Pitfall2.class, "field", int.class); } catch (Exception e) { throw new Error(e); } } } /** * 陷阱3:ABA 问题 * 解决:使用版本号或指针标记 */ class Pitfall3 { // 使用带版本号的引用 private static class VersionedRef<T> { final T ref; final long version; // ... } } /** * 陷阱4:内存泄漏 * 解决:及时清理无用节点引用 */ class Pitfall4 { void safePop(Node<E> node) { // 弹出后清理引用 node.item = null; node.next = null; // 帮助 GC } } /** * 陷阱5:活锁(live lock) * 解决:引入随机退避或队列机制 */ class Pitfall5 { void operationWithBackoff() { int attempts = 0; while (!tryOperation()) { attempts++; if (attempts > MAX_ATTEMPTS) { // 退避或抛出异常 exponentialBackoff(attempts); } } } private void exponentialBackoff(int attempts) { try { Thread.sleep((1L << Math.min(attempts, 10))); // 指数退避 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } }
3. 性能监控与调优
java
复制
下载
import java.util.concurrent.atomic.LongAdder; public class PerformanceMonitor { /** * 性能统计 */ public static class Stats { // 使用 LongAdder 代替 AtomicLong,减少竞争 private final LongAdder operations = new LongAdder(); private final LongAdder casFailures = new LongAdder(); private final LongAdder retries = new LongAdder(); // 使用 VarHandle 保护时间戳 private volatile long startTime = 0; private static final VarHandle START_TIME_HANDLE; static { try { START_TIME_HANDLE = MethodHandles.lookup() .findVarHandle(Stats.class, "startTime", long.class); } catch (Exception e) { throw new Error(e); } } public void recordOperation() { operations.increment(); } public void recordCASFailure() { casFailures.increment(); } public void recordRetry() { retries.increment(); } public void startTiming() { START_TIME_HANDLE.setVolatile(this, System.nanoTime()); } public long getElapsedNanos() { long start = (long) START_TIME_HANDLE.getAcquire(this); return System.nanoTime() - start; } public void printStats() { long ops = operations.sum(); long failures = casFailures.sum(); long retryCount = retries.sum(); double failureRate = ops > 0 ? (double) failures / ops : 0; System.out.printf("Operations: %,d%n", ops); System.out.printf("CAS failures: %,d (%.2f%%)%n", failures, failureRate * 100); System.out.printf("Retries: %,d%n", retryCount); System.out.printf("Elapsed time: %.2f ms%n", getElapsedNanos() / 1e6); if (ops > 0) { System.out.printf("Throughput: %.2f ops/ms%n", ops / (getElapsedNanos() / 1e6)); } } } /** * 监控装饰器 */ public static class MonitoredLockFreeStack<E> extends LockFreeStack<E> { private final Stats stats = new Stats(); @Override public void push(E item) { stats.startTiming(); int attempts = 0; while (true) { attempts++; stats.recordOperation(); try { super.push(item); return; } catch (Exception e) { stats.recordCASFailure(); if (attempts > 10) { stats.recordRetry(); // 指数退避 try { Thread.sleep(1L << Math.min(attempts - 10, 4)); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); throw new RuntimeException(ie); } } } } } public Stats getStats() { return stats; } } }
八、总结
VarHandle 无锁数据结构优势:
- 类型安全 - 编译期检查,避免 Unsafe 的类型错误
- 内存语义明确 - 清晰的 acquire/release/volatile 语义
- 性能优异 - 接近原生操作的性能
- 平台兼容 - 在 AOT 编译和未来 JDK 版本中支持更好
- 代码可维护 - 标准 API,易于理解和维护
使用建议:
- 对于新建项目,优先使用 VarHandle 而不是 Unsafe
- 理解不同内存语义的适用场景
- 注意处理 ABA 问题和内存泄漏
- 进行充分的并发测试和性能测试
- 结合缓存行填充等优化技巧
通过 VarHandle 实现的无锁数据结构,可以在保证线程安全的同时,获得接近最优的性能表现,是现代 Java 并发编程的重要工具。