腾讯Java面试被问:阻塞队列BlockingQueue的实现原理

一、阻塞队列核心概念

1. 阻塞队列的定义与特性

java

复制代码
// BlockingQueue 接口定义的核心方法
public interface BlockingQueue<E> extends Queue<E> {
    /*
    阻塞队列的四大核心操作:
    
    1. 插入操作(可能阻塞)
       - put(E e): 队列满时阻塞,直到有空间
       - offer(E e, long timeout, TimeUnit unit): 限时等待
    
    2. 移除操作(可能阻塞)
       - take(): 队列空时阻塞,直到有元素
       - poll(long timeout, TimeUnit unit): 限时等待
    
    3. 检查操作(非阻塞)
       - peek(): 查看队首元素,不删除
       - size(): 返回队列当前元素数
    
    4. 特殊操作
       - drainTo(Collection<? super E> c): 批量转移
       - remainingCapacity(): 剩余容量
    */
}

2. Java内置阻塞队列对比

text

复制代码
Java并发包提供的阻塞队列实现:

1. ArrayBlockingQueue(有界数组队列)
   - 基于数组的FIFO队列
   - 固定容量,可选公平/非公平锁

2. LinkedBlockingQueue(可选有界链表队列)
   - 基于链表的FIFO队列
   - 默认无界(Integer.MAX_VALUE),可指定容量

3. PriorityBlockingQueue(优先级队列)
   - 无界队列,元素按优先级排序
   - 基于堆实现,非FIFO

4. DelayQueue(延迟队列)
   - 无界队列,元素延迟到期
   - 基于PriorityQueue实现

5. SynchronousQueue(同步队列)
   - 不存储元素的阻塞队列
   - 每个插入操作必须等待移除操作

6. LinkedTransferQueue(转移队列)
   - 无界队列,支持"匹配"语义
   - 结合了SynchronousQueue和LinkedBlockingQueue特性

二、核心实现机制

1. 阻塞队列的底层依赖

java

复制代码
// 所有阻塞队列实现的核心机制
public class BlockingMechanism {
    /*
    三大核心机制:
    
    1. 锁机制(Lock)
       - ReentrantLock:保证线程安全
       - 条件变量(Condition):实现阻塞/唤醒
    
    2. 等待/通知模式
       - notEmpty:队列空时等待,非空时唤醒
       - notFull:队列满时等待,不满时唤醒
    
    3. 数据结构
       - 数组:ArrayBlockingQueue
       - 链表:LinkedBlockingQueue
       - 堆:PriorityBlockingQueue
    */
}

三、ArrayBlockingQueue 源码解析

1. 内部数据结构

java

复制代码
// ArrayBlockingQueue 的核心字段
public class ArrayBlockingQueue<E> extends AbstractQueue<E> 
    implements BlockingQueue<E>, java.io.Serializable {
    
    // 1. 核心数据结构:循环数组
    final Object[] items;
    
    // 2. 指针:队首和队尾
    int takeIndex;  // 下一个要取元素的位置
    int putIndex;   // 下一个要放元素的位置
    int count;      // 队列中元素数量
    
    // 3. 锁与条件变量
    final ReentrantLock lock;
    private final Condition notEmpty;  // 队列非空条件
    private final Condition notFull;   // 队列非满条件
    
    // 4. 可选公平性策略
    public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = new Object[capacity];
        lock = new ReentrantLock(fair);  // 公平/非公平锁
        notEmpty = lock.newCondition();
        notFull = lock.newCondition();
    }
}

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】

2. put() 方法实现(阻塞插入)

java

复制代码
// 核心阻塞插入方法
public void put(E e) throws InterruptedException {
    Objects.requireNonNull(e);  // 检查空值
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();   // 可中断加锁
    
    try {
        // 如果队列满,在notFull条件上等待
        while (count == items.length) {
            notFull.await();  // 释放锁并等待,被唤醒后重新获取锁
        }
        enqueue(e);  // 队列不满,执行入队
    } finally {
        lock.unlock();  // 确保锁释放
    }
}

// 入队操作(私有方法)
private void enqueue(E x) {
    final Object[] items = this.items;
    items[putIndex] = x;  // 放入元素
    // 循环数组:到达末尾后回到开头
    if (++putIndex == items.length)
        putIndex = 0;
    count++;  // 元素计数增加
    
    // 唤醒在notEmpty上等待的消费者线程
    notEmpty.signal();
}

3. take() 方法实现(阻塞移除)

java

复制代码
// 核心阻塞移除方法
public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();  // 可中断加锁
    
    try {
        // 如果队列空,在notEmpty条件上等待
        while (count == 0) {
            notEmpty.await();  // 释放锁并等待
        }
        return dequeue();  // 队列不空,执行出队
    } finally {
        lock.unlock();
    }
}

// 出队操作(私有方法)
private E dequeue() {
    final Object[] items = this.items;
    @SuppressWarnings("unchecked")
    E x = (E) items[takeIndex];  // 取出元素
    
    items[takeIndex] = null;  // 置空,帮助GC
    // 循环数组:到达末尾后回到开头
    if (++takeIndex == items.length)
        takeIndex = 0;
    count--;  // 元素计数减少
    
    // 如果使用了迭代器,可能需要维护迭代器状态
    if (itrs != null)
        itrs.elementDequeued();
    
    // 唤醒在notFull上等待的生产者线程
    notFull.signal();
    return x;
}

4. 循环数组工作原理

java

复制代码
// 循环数组的索引计算
public class CircularArray {
    /*
    初始状态:
    items = [null, null, null, null, null]  // 容量5
    takeIndex = 0, putIndex = 0, count = 0
    
    操作序列:
    1. put(A) → items[0] = A, putIndex=1, count=1
       数组:[A, null, null, null, null]
    
    2. put(B) → items[1] = B, putIndex=2, count=2
       数组:[A, B, null, null, null]
    
    3. take() → 取走A, takeIndex=1, count=1
       数组:[null, B, null, null, null]
    
    4. put(C) → items[2] = C, putIndex=3, count=2
       数组:[null, B, C, null, null]
    
    5. 一直put到满,再take到空...
    
    循环机制:
    putIndex到达末尾后:if (++putIndex == items.length) putIndex = 0;
    takeIndex同理
    
    判断满:count == items.length
    判断空:count == 0
    */
}

5. 公平性策略影响

java

复制代码
// 公平锁 vs 非公平锁对性能的影响
public class FairnessComparison {
    /*
    公平锁(fair = true):
    - 线程按请求锁的顺序获得锁
    - 避免线程饥饿
    - 性能较低(需要维护队列)
    
    非公平锁(fair = false,默认):
    - 线程竞争获取锁,可能插队
    - 可能造成某些线程饥饿
    - 性能较高(吞吐量大)
    
    生产环境建议:
    - 高并发场景:非公平锁(性能优先)
    - 对响应时间敏感:公平锁(公平性优先)
    - 默认使用非公平锁,除非有特殊需求
    */
}

四、LinkedBlockingQueue 源码解析

1. 内部数据结构

java

复制代码
// LinkedBlockingQueue 节点定义
public class LinkedBlockingQueue<E> extends AbstractQueue<E>
    implements BlockingQueue<E>, java.io.Serializable {
    
    // 1. 节点类(静态内部类)
    static class Node<E> {
        E item;          // 存储的数据
        Node<E> next;    // 下一个节点引用
        
        Node(E x) { item = x; }
    }
    
    // 2. 容量控制
    private final int capacity;  // 队列容量(默认Integer.MAX_VALUE)
    private final AtomicInteger count = new AtomicInteger();  // 当前元素数
    
    // 3. 双锁设计(提升并发性能)
    private final ReentrantLock takeLock = new ReentrantLock();
    private final Condition notEmpty = takeLock.newCondition();
    
    private final ReentrantLock putLock = new ReentrantLock();
    private final Condition notFull = putLock.newCondition();
    
    // 4. 头尾指针
    transient Node<E> head;  // 头节点(总是哑节点)
    private transient Node<E> last;   // 尾节点
    
    // 构造方法
    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);  // 默认无界
    }
    
    public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node<E>(null);  // 初始化哑节点
    }
}

2. 双锁分离设计

java

复制代码
// LinkedBlockingQueue 的并发优化:双锁分离
public class TwoLockDesign {
    /*
    为什么使用双锁?
    
    1. 传统单锁问题:
       - put和take操作都需要获取同一把锁
       - 生产者和消费者互相阻塞
       - 并发性能受限
    
    2. 双锁分离优势:
       - 读锁(takeLock):控制出队操作
       - 写锁(putLock):控制入队操作
       - 生产者和消费者可以并行操作(除非队列空/满)
    
    3. 特殊情况处理:
       - 队列从空变为非空:需要获取读锁唤醒消费者
       - 队列从满变为不满:需要获取写锁唤醒生产者
    
    性能对比(4核8线程):
    - ArrayBlockingQueue:约 80万 ops/sec
    - LinkedBlockingQueue:约 150万 ops/sec
    */
}

3. put() 方法实现

java

复制代码
// LinkedBlockingQueue 的 put 方法
public void put(E e) throws InterruptedException {
    if (e == null) throw new NullPointerException();
    
    int c = -1;  // 用于记录操作前的元素数
    Node<E> node = new Node<E>(e);  // 创建新节点
    final ReentrantLock putLock = this.putLock;
    final AtomicInteger count = this.count;
    
    putLock.lockInterruptibly();  // 获取写锁
    
    try {
        // 如果队列满,等待
        while (count.get() == capacity) {
            notFull.await();
        }
        
        // 入队操作
        enqueue(node);
        
        // 原子增加计数,并获取旧值
        c = count.getAndIncrement();
        
        // 如果插入后队列未满,唤醒其他生产者
        if (c + 1 < capacity) {
            notFull.signal();
        }
    } finally {
        putLock.unlock();
    }
    
    // 如果插入前队列为空,唤醒消费者
    if (c == 0) {
        signalNotEmpty();
    }
}

// 入队操作
private void enqueue(Node<E> node) {
    // 将新节点链接到尾部
    last = last.next = node;
}

// 唤醒等待的消费者
private void signalNotEmpty() {
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lock();  // 必须获取读锁才能发信号
    try {
        notEmpty.signal();
    } finally {
        takeLock.unlock();
    }
}

4. take() 方法实现

java

复制代码
// LinkedBlockingQueue 的 take 方法
public E take() throws InterruptedException {
    E x;
    int c = -1;  // 用于记录操作前的元素数
    final AtomicInteger count = this.count;
    final ReentrantLock takeLock = this.takeLock;
    
    takeLock.lockInterruptibly();  // 获取读锁
    
    try {
        // 如果队列空,等待
        while (count.get() == 0) {
            notEmpty.await();
        }
        
        // 出队操作
        x = dequeue();
        
        // 原子减少计数,并获取旧值
        c = count.getAndDecrement();
        
        // 如果取出后队列不空,唤醒其他消费者
        if (c > 1) {
            notEmpty.signal();
        }
    } finally {
        takeLock.unlock();
    }
    
    // 如果取出前队列为满,唤醒生产者
    if (c == capacity) {
        signalNotFull();
    }
    
    return x;
}

// 出队操作
private E dequeue() {
    Node<E> h = head;         // 当前头节点(哑节点)
    Node<E> first = h.next;   // 第一个实际数据节点
    
    // 断开哑节点
    h.next = h;  // help GC(将哑节点指向自己)
    
    // 更新头节点
    head = first;
    E x = first.item;
    first.item = null;  // 帮助GC
    return x;
}

// 唤醒等待的生产者
private void signalNotFull() {
    final ReentrantLock putLock = this.putLock;
    putLock.lock();  // 必须获取写锁才能发信号
    try {
        notFull.signal();
    } finally {
        putLock.unlock();
    }
}

五、其他阻塞队列实现原理

1. PriorityBlockingQueue(优先级队列)

java

复制代码
// 基于堆的优先级队列实现
public class PriorityBlockingQueue<E> extends AbstractQueue<E>
    implements BlockingQueue<E>, java.io.Serializable {
    
    // 1. 核心数据结构:二叉堆
    private transient Object[] queue;  // 堆数组
    private transient int size;        // 元素数量
    private final Comparator<? super E> comparator;  // 比较器
    
    // 2. 锁机制(只有一个锁,因为需要维护堆结构)
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition notEmpty = lock.newCondition();
    
    // 3. 扩容机制(无界队列)
    private static final int DEFAULT_INITIAL_CAPACITY = 11;
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    
    // 出队(总是返回优先级最高的元素)
    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        E result;
        try {
            // 如果队列空,等待
            while ((result = dequeue()) == null) {
                notEmpty.await();
            }
        } finally {
            lock.unlock();
        }
        return result;
    }
    
    // 堆的下沉操作(维护堆性质)
    private void siftDownComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>)x;
        int half = size >>> 1;  // 最后一个非叶子节点
        
        while (k < half) {
            int child = (k << 1) + 1;  // 左孩子索引
            Object c = queue[child];   // 左孩子
            int right = child + 1;     // 右孩子索引
            
            // 选择较小的孩子
            if (right < size &&
                ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0) {
                c = queue[child = right];
            }
            
            // 如果x小于等于孩子,堆性质满足
            if (key.compareTo((E) c) <= 0) {
                break;
            }
            
            // 否则,孩子上移
            queue[k] = c;
            k = child;
        }
        queue[k] = key;
    }
}

2. SynchronousQueue(同步队列)

java

复制代码
// 不存储元素的阻塞队列
public class SynchronousQueue<E> extends AbstractQueue<E>
    implements BlockingQueue<E>, java.io.Serializable {
    
    /*
    核心特性:
    1. 不存储元素,每个put必须等待一个take
    2. 可以看作容量为1的队列,但连这个"1"都不存储
    3. 支持公平模式(队列)和非公平模式(栈)
    
    实现原理:
    1. 双数据结构模式:
       - TransferStack(非公平,LIFO)
       - TransferQueue(公平,FIFO)
    
    2. 核心Transfer接口:
       - transfer(E e, boolean timed, long nanos)
       - 如果当前有等待的消费者,直接传递
       - 否则,生产者等待
    
    适用场景:
    - 线程间直接传递数据
    - Executors.newCachedThreadPool() 使用
    */
    
    // 核心:TransferStack节点
    static final class SNode {
        volatile SNode next;        // 下一个节点
        volatile SNode match;       // 匹配的节点
        volatile Thread waiter;     // 等待的线程
        Object item;                // 数据项
        int mode;                   // 模式:REQUEST(消费者)或DATA(生产者)
    }
    
    // 核心transfer方法(简化版)
    E transfer(E e, boolean timed, long nanos) {
        SNode s = null;
        int mode = (e == null) ? REQUEST : DATA;  // 判断是生产者还是消费者
        
        for (;;) {  // 自旋
            SNode h = head;
            if (h == null || h.mode == mode) {  // 栈空或相同模式
                // 等待匹配
                if (timed && nanos <= 0) {  // 超时
                    if (s != null && s.cancel())
                        clean(s);
                    return null;
                }
                // 创建节点并入栈
                s = new SNode(e);
                if (!casHead(h, s))  // CAS更新栈顶
                    continue;
                // 等待匹配
                SNode m = awaitFulfill(s, timed, nanos);
                if (m == s) {  // 等待被取消
                    clean(s);
                    return null;
                }
                // 匹配成功,返回数据
                return (E) ((mode == REQUEST) ? m.item : s.item);
            } else {  // 不同模式,可以匹配
                SNode m = h.next;
                if (m == null)  // 没有等待者
                    continue;
                // 尝试匹配
                if (casHead(h, m.next)) {  // 弹出匹配的节点
                    // 唤醒等待的线程
                    LockSupport.unpark(m.waiter);
                    return (E) ((mode == REQUEST) ? m.item : s.item);
                }
            }
        }
    }
}

六、阻塞队列的性能优化技巧

1. 减少锁竞争的策略

java

复制代码
// 1. 批量操作减少锁获取次数
public class BatchOperations {
    // 批量插入
    public void batchPut(List<E> items) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            for (E item : items) {
                while (count == items.length) {
                    notFull.await();
                }
                enqueue(item);
            }
        } finally {
            lock.unlock();
        }
    }
    
    // 批量消费
    public List<E> batchTake(int maxElements) {
        List<E> result = new ArrayList<>(maxElements);
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            for (int i = 0; i < maxElements; i++) {
                while (count == 0) {
                    notEmpty.await();
                }
                result.add(dequeue());
            }
        } finally {
            lock.unlock();
        }
        return result;
    }
}

// 2. 使用drainTo方法
public int drainTo(Collection<? super E> c, int maxElements) {
    // 一次性转移多个元素,减少锁开销
    // 实现中通常只获取一次锁
}

2. 避免虚假唤醒

java

复制代码
// 条件等待的正确模式
public class CorrectAwaitPattern {
    /*
    虚假唤醒(Spurious Wakeup):
    - 线程可能在没有被signal的情况下从await()返回
    - POSIX标准允许,JVM也可能发生
    
    正确做法:总是使用while循环检查条件
    */
    
    // ❌ 错误做法:if判断
    public void wrongPut(E e) throws InterruptedException {
        lock.lock();
        try {
            if (queue.isFull()) {  // if判断可能被虚假唤醒
                notFull.await();
            }
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }
    
    // ✅ 正确做法:while循环
    public void correctPut(E e) throws InterruptedException {
        lock.lock();
        try {
            while (queue.isFull()) {  // while循环抵御虚假唤醒
                notFull.await();
            }
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }
}

3. 性能对比与选择建议

markdown

复制代码
| 队列类型 | 优点 | 缺点 | 适用场景 |
|---------|------|------|---------|
| **ArrayBlockingQueue** | 内存连续,缓存友好 | 容量固定,扩容麻烦 | 固定容量,性能要求高 |
| **LinkedBlockingQueue** | 动态扩容,吞吐量高 | 内存不连续,节点开销 | 无界或大容量队列 |
| **PriorityBlockingQueue** | 优先级处理 | 排序开销,非FIFO | 任务调度,优先级处理 |
| **SynchronousQueue** | 直接传递,零存储 | 必须有配对线程 | 线程池工作队列 |
| **DelayQueue** | 延迟执行 | 排序开销 | 定时任务,缓存过期 |

选择建议:
1. 固定容量高吞吐 → ArrayBlockingQueue
2. 无界/大容量 → LinkedBlockingQueue
3. 优先级调度 → PriorityBlockingQueue
4. 线程间直接传递 → SynchronousQueue
5. 延迟任务 → DelayQueue

七、阻塞队列在生产环境的实践

1. 线程池的工作队列

java

复制

下载

复制代码
// ThreadPoolExecutor 如何使用阻塞队列
public class ThreadPoolExecutorExample {
    
    // 1. FixedThreadPool 使用 LinkedBlockingQueue(无界)
    ExecutorService fixedPool = Executors.newFixedThreadPool(10);
    // 内部实现:new ThreadPoolExecutor(nThreads, nThreads, 
    //                                0L, TimeUnit.MILLISECONDS,
    //                                new LinkedBlockingQueue<Runnable>());
    
    // 2. CachedThreadPool 使用 SynchronousQueue
    ExecutorService cachedPool = Executors.newCachedThreadPool();
    // 内部实现:new ThreadPoolExecutor(0, Integer.MAX_VALUE,
    //                                60L, TimeUnit.SECONDS,
    //                                new SynchronousQueue<Runnable>());
    
    // 3. SingleThreadExecutor 使用 LinkedBlockingQueue
    ExecutorService singlePool = Executors.newSingleThreadExecutor();
    
    // 4. ScheduledThreadPool 使用 DelayedWorkQueue
    ScheduledExecutorService scheduledPool = 
        Executors.newScheduledThreadPool(5);
}

2. 生产者-消费者模式实现

java

复制代码
// 使用阻塞队列实现生产者-消费者
public class ProducerConsumerExample {
    
    private final BlockingQueue<Task> queue = new LinkedBlockingQueue<>(100);
    
    // 生产者
    class Producer implements Runnable {
        private final Random random = new Random();
        
        @Override
        public void run() {
            try {
                while (!Thread.currentThread().isInterrupted()) {
                    // 生产任务
                    Task task = generateTask();
                    
                    // 放入队列(队列满时阻塞)
                    queue.put(task);
                    System.out.println("生产任务: " + task);
                    
                    // 控制生产速度
                    Thread.sleep(random.nextInt(1000));
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
    
    // 消费者
    class Consumer implements Runnable {
        @Override
        public void run() {
            try {
                while (!Thread.currentThread().isInterrupted()) {
                    // 从队列取任务(队列空时阻塞)
                    Task task = queue.take();
                    System.out.println("消费任务: " + task);
                    
                    // 处理任务
                    processTask(task);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
    
    // 启动生产者和消费者
    public void start() {
        ExecutorService executor = Executors.newCachedThreadPool();
        executor.execute(new Producer());
        executor.execute(new Producer());
        executor.execute(new Consumer());
        executor.execute(new Consumer());
    }
}

3. 流量控制与限流

java

复制代码
// 使用阻塞队列实现简单的限流
public class RateLimiterWithBlockingQueue {
    
    private final BlockingQueue<Object> tokenBucket;
    
    public RateLimiterWithBlockingQueue(int permitsPerSecond) {
        // 创建指定容量的队列
        tokenBucket = new ArrayBlockingQueue<>(permitsPerSecond);
        
        // 初始化令牌
        for (int i = 0; i < permitsPerSecond; i++) {
            tokenBucket.offer(new Object());
        }
        
        // 定时补充令牌
        ScheduledExecutorService scheduler = 
            Executors.newSingleThreadScheduledExecutor();
        scheduler.scheduleAtFixedRate(() -> {
            try {
                tokenBucket.put(new Object());
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }, 0, 1_000_000_000L / permitsPerSecond, TimeUnit.NANOSECONDS);
    }
    
    // 获取令牌(阻塞直到获取)
    public void acquire() throws InterruptedException {
        tokenBucket.take();  // 队列空时阻塞
    }
    
    // 尝试获取令牌(非阻塞)
    public boolean tryAcquire() {
        return tokenBucket.poll() != null;
    }
    
    // 限时获取令牌
    public boolean tryAcquire(long timeout, TimeUnit unit) 
            throws InterruptedException {
        return tokenBucket.poll(timeout, unit) != null;
    }
}

八、常见问题与解决方案

1. 死锁问题

java

复制代码
// 错误的使用方式可能导致死锁
public class DeadlockExample {
    
    private final BlockingQueue<String> queue1 = new ArrayBlockingQueue<>(10);
    private final BlockingQueue<String> queue2 = new ArrayBlockingQueue<>(10);
    
    // 线程1:先取queue1,再取queue2
    Thread thread1 = new Thread(() -> {
        try {
            String item1 = queue1.take();  // 可能阻塞
            String item2 = queue2.take();  // 可能永远阻塞
            System.out.println(item1 + item2);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    });
    
    // 线程2:先取queue2,再取queue1
    Thread thread2 = new Thread(() -> {
        try {
            String item2 = queue2.take();  // 可能阻塞
            String item1 = queue1.take();  // 可能永远阻塞
            System.out.println(item1 + item2);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    });
    
    // 解决方案:统一获取顺序,或使用超时
    public void solution() {
        // 方案1:统一获取顺序
        // 方案2:使用poll(timeout)而不是take()
        // 方案3:使用tryTransfer模式
    }
}

2. 内存泄漏问题

java

复制代码
// LinkedBlockingQueue 的迭代器可能造成内存泄漏
public class MemoryLeakExample {
    
    /*
    问题:LinkedBlockingQueue的迭代器会保留引用
    当元素被移除后,迭代器仍可能引用该元素
    
    解决方案:
    1. 及时关闭迭代器
    2. 使用弱引用
    3. 定期清理
    */
    
    public void safeIteration() {
        LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<>();
        
        // 错误的迭代方式
        Iterator<String> iterator = queue.iterator();
        while (iterator.hasNext()) {
            String item = iterator.next();
            // 处理item...
        }
        // 迭代器仍然持有queue的引用
        
        // 正确的做法:限制迭代器作用域
        try {
            Iterator<String> safeIterator = queue.iterator();
            while (safeIterator.hasNext()) {
                String item = safeIterator.next();
                // 处理item...
            }
        } finally {
            // 迭代器超出作用域,可被GC
        }
    }
}

3. 性能调优参数

bash

复制代码
# JVM参数调优建议

# 1. 减少上下文切换
-XX:+UseSpinning              # 启用自旋锁
-XX:PreBlockSpin=10           # 自旋次数

# 2. 内存优化
-XX:+UseCompressedOops        # 压缩指针
-XX:+UseG1GC                  # G1垃圾收集器
-XX:MaxGCPauseMillis=200      # 最大GC停顿

# 3. 锁优化
-XX:+UseBiasedLocking         # 偏向锁
-XX:BiasedLockingStartupDelay=0  # 立即启用偏向锁

# 4. 线程池优化(配合使用)
-XX:ActiveProcessorCount=4    # 指定CPU核心数

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】


九、面试考点总结

1. 高频面试问题

java

复制代码
public class InterviewQuestions {
    /*
    1. **基本原理**:
       - 阻塞队列是如何实现阻塞的?
       - ArrayBlockingQueue和LinkedBlockingQueue的区别?
       - 为什么LinkedBlockingQueue使用双锁?
    
    2. **源码实现**:
       - put()和take()方法的具体实现?
       - 条件变量Condition是如何使用的?
       - 虚假唤醒是什么?如何避免?
    
    3. **性能优化**:
       - 如何选择不同的阻塞队列实现?
       - 阻塞队列的性能瓶颈在哪里?
       - 如何避免死锁和内存泄漏?
    
    4. **应用场景**:
       - 线程池是如何使用阻塞队列的?
       - 如何用阻塞队列实现生产者-消费者模式?
       - 如何用阻塞队列实现限流?
    
    5. **并发控制**:
       - 公平锁和非公平锁的区别?
       - 双锁分离如何提升性能?
       - CAS在阻塞队列中的应用?
    */
}

2. 手写简单阻塞队列

java

复制代码
// 面试常考:手写一个简单的阻塞队列
public class SimpleBlockingQueue<E> {
    
    private final Object[] items;
    private int putIndex, takeIndex, count;
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition notEmpty = lock.newCondition();
    private final Condition notFull = lock.newCondition();
    
    public SimpleBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.items = new Object[capacity];
    }
    
    public void put(E e) throws InterruptedException {
        Objects.requireNonNull(e);
        lock.lockInterruptibly();
        try {
            while (count == items.length) {
                notFull.await();
            }
            items[putIndex] = e;
            if (++putIndex == items.length) putIndex = 0;
            count++;
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }
    
    public E take() throws InterruptedException {
        lock.lockInterruptibly();
        try {
            while (count == 0) {
                notEmpty.await();
            }
            @SuppressWarnings("unchecked")
            E x = (E) items[takeIndex];
            items[takeIndex] = null;
            if (++takeIndex == items.length) takeIndex = 0;
            count--;
            notFull.signal();
            return x;
        } finally {
            lock.unlock();
        }
    }
    
    public int size() {
        lock.lock();
        try {
            return count;
        } finally {
            lock.unlock();
        }
    }
}

总结 :阻塞队列是Java并发编程的核心组件,理解其实现原理对于编写高性能并发程序至关重要。关键要掌握锁机制、条件变量、线程安全等核心概念,并能根据实际场景选择合适的阻塞队列实现。

相关推荐
京东零售技术2 小时前
15 年评价中台如何涅槃?超百亿数据×千万 QPM×百万行代码的重构全景复盘
后端
曲幽2 小时前
Flask登录验证实战:从零构建一个基础的账号密码登录系统
python·flask·web·session·username·login
superman超哥2 小时前
仓颉类型别名的使用方法深度解析
c语言·开发语言·c++·python·仓颉
廋到被风吹走2 小时前
【Spring】BeanPostProcessor详解
java·后端·spring
bbq粉刷匠2 小时前
二叉树中两个指定节点的最近公共祖先
java·算法
卡尔特斯2 小时前
pyenv 安装的 python 版本缺少 tkinter 报错 import _tkinter # If this fails your Python xxx
python
3824278272 小时前
python :__call__方法
开发语言·python
ppo922 小时前
Spring Boot 集成 Kafka 3.9.0:部署、监控与消息发送教程
java·架构
爱分享的鱼鱼2 小时前
完整理解乐观锁(以预定系统为例)
后端