并发编程原理与实战(三十九)并发基石ArrayBlockingQueue与LinkedBlockingQueue的底层实现与API设计解析

《Java源码分析》:BlockingQueue之ArrayBlockingQueue 《Java源码分析》:BlockingQueue2016年8月27日21:10:51,今天是学校报道的第一天,从今天开始,我也就研三了,哎,有时候只能感叹时间过的真快,自己的研究生生活只剩下最后一年。在最后的一年里,希望自己的一切都顺利吧,其中包括:找一份好的工作,顺利将手上的项目完成,顺利完成自己的毕设。说了这么多,还是开始本篇博文的正题吧。在前一段时间研究了下J.U.C包下面并发类库的原理,也都 阅读详情

本文继续学习另外两个并发工具类ArrayBlockingQueue和LinkedBlockingQueue。

ArrayBlockingQueue核心特性与原理

ArrayBlockingQueue是Java并发编程中一个重要的有界阻塞队列,基于数组实现,采用单个ReentrantLock控制线程安全,非常适合生产者-消费者场景。具有有界队列、线程安全、先进先出等特性。

有界队列‌

在这里插入图片描述

ArrayBlockingQueue的三个构造函数都必须传入队列容量参数,创建对比时必须指定容量,无法动态扩容。

线程安全‌

/*
 * Concurrency control uses the classic two-condition algorithm
 * found in any textbook.
 */

/** Main lock guarding all access */
final ReentrantLock lock;

/** Condition for waiting takes */
@SuppressWarnings("serial")  // Classes implementing Condition may be serializable.
private final Condition notEmpty;

/** Condition for waiting puts */
@SuppressWarnings("serial")  // Classes implementing Condition may be serializable.
private final Condition notFull;

......

    /**
 * Creates an {@code ArrayBlockingQueue} with the given (fixed)
 * capacity and the specified access policy.
 *
 * @param capacity the capacity of this queue
 * @param fair if {@code true} then queue accesses for threads blocked
 *        on insertion or removal, are processed in FIFO order;
 *        if {@code false} the access order is unspecified.
 * @throws IllegalArgumentException if {@code capacity < 1}
 */
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();
}

从源码可以看出,ArrayBlockingQueue采用了经典的两条件算法,通过ReentrantLock和Condition实现线程间的阻塞与唤醒,队列满时阻塞生产者,队列空时阻塞消费者。notFull‌条件控制生产者线程的等待和唤醒,notEmpty‌条件控制消费者线程的等待和唤醒。

FIFO原则‌

遵循先进先出的队列顺序。 FIFO 特性是通过一个‌循环数组‌来实现的,其核心在于两个指针:takeIndex 和 putIndex。

/** The queued items */
@SuppressWarnings("serial") // Conditionally serializable
final Object[] items;

/** items index for next take, poll, peek or remove */
int takeIndex;

/** items index for next put, offer, or add */
int putIndex;

/** Number of elements in the queue */
int count;

item 是存储队列元素的数组,takeIndex是下一个被取出元素的索引,putIndex是下一个被放入元素的索引,count为队列中的元素数量。

ArrayBlockingQueue核心API方法

入队方法

ArrayBlockingQueue提供了多种入队的方法,其主要区别在队列满时行为、返回值。

在这里插入图片描述

(1)add(E e)方法

/**
 * Inserts the specified element at the tail of this queue if it is
 * possible to do so immediately without exceeding the queue's capacity,
 * returning {@code true} upon success and throwing an
 * {@code IllegalStateException} if this queue is full.
 *
 * @param e the element to add
 * @return {@code true} (as specified by {@link Collection#add})
 * @throws IllegalStateException if this queue is full
 * @throws NullPointerException if the specified element is null
 */
public boolean add(E e) {
    return super.add(e);
}

add(E e)方法往队列尾部插入一个元素,如果队列满时将抛出IllegalStateException异常,插入的元素为空则抛出NullPointerException,插入成功返回true,否则返回false,立即返回。其内部最终调用offer(E e)实现。

(2)offer(E e)方法

/**
 * Inserts the specified element at the tail of this queue if it is
 * possible to do so immediately without exceeding the queue's capacity,
 * returning {@code true} upon success and {@code false} if this queue
 * is full.  This method is generally preferable to method {@link #add},
 * which can fail to insert an element only by throwing an exception.
 *
 * @throws NullPointerException if the specified element is null
 */
public boolean offer(E e) {
    Objects.requireNonNull(e);
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        if (count == items.length)
            return false;
        else {
            enqueue(e);
            return true;
        }
    } finally {
        lock.unlock();
    }
}

offer(E e)采用了ReentrantLock对插入元素的过程进行加锁。插入的元素为空则抛出NullPointerException,插入成功返回true,否则返回false,立即返回。该方法通常优于方法add,因为add方法在无法插入元素时只能通过抛出异常来反馈失败。

(3)offer(E e, long timeout, TimeUnit unit)方法

/**
 * Inserts the specified element at the tail of this queue, waiting
 * up to the specified wait time for space to become available if
 * the queue is full.
 *
 * @throws InterruptedException {@inheritDoc}
 * @throws NullPointerException {@inheritDoc}
 */
public boolean offer(E e, long timeout, TimeUnit unit)
    throws InterruptedException {

    Objects.requireNonNull(e);
    long nanos = unit.toNanos(timeout);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == items.length) {
            if (nanos <= 0L)
                return false;
            nanos = notFull.awaitNanos(nanos);
        }
        enqueue(e);
        return true;
    } finally {
        lock.unlock();
    }
}

offer(E e, long timeout, TimeUnit unit)方法则在队列满时等待指定的时间,直到时间消耗完后才返回,插入成功返回true,否则返回false。

(4)put(E e)方法

/**
 * Inserts the specified element at the tail of this queue, waiting
 * for space to become available if the queue is full.
 *
 * @throws InterruptedException {@inheritDoc}
 * @throws NullPointerException {@inheritDoc}
 */
public void put(E e) throws InterruptedException {
    Objects.requireNonNull(e);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == items.length)
            notFull.await();
        enqueue(e);
    } finally {
        lock.unlock();
    }
}

put(E e)方法插入时,判断队列是否已满,满了就一直阻塞等待直到能插入元素,该方法没有返回值。从上述源码可以看出,入队操作的这几个方法最终都是调用私有的enqueue(E x)方法。

private void enqueue(E x) {
    final Object[] items = this.items;
    items[putIndex] = x; // 将元素放入 putIndex 位置
    if (++putIndex == items.length) // putIndex 指针后移,如果到达数组末尾...
        putIndex = 0; // ...则绕回数组开头(循环)
    count++; // 元素数量增加
    notEmpty.signal(); // 唤醒可能正在等待获取元素的消费者线程
}

出队方法

ArrayBlockingQueue同样提供了多个元素出队方法,主要区别在是否立即返回、阻塞等待、超时控制。

在这里插入图片描述

// 有元素立即返回,否则返回null
public E poll() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        return (count == 0) ? null : dequeue();
    } finally {
        lock.unlock();
    }
}
// 有元素立即返回,否则等待指定长的时间
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
    long nanos = unit.toNanos(timeout);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == 0) {
            if (nanos <= 0L)
                return null;
            nanos = notEmpty.awaitNanos(nanos);
        }
        return dequeue();
    } finally {
        lock.unlock();
    }
}
//阻塞等待直到有元素可以返回
public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == 0)
            notEmpty.await();
        return dequeue();
    } finally {
        lock.unlock();
    }
}

这四个方法最终都是调用私有的核心出队方法dequeue()。

private E dequeue() {
    final Object[] items = this.items;
    @SuppressWarnings("unchecked")
    E x = (E) items[takeIndex]; // 从 takeIndex 位置取出元素
    items[takeIndex] = null; // 清空该位置,帮助GC
    if (++takeIndex == items.length) // takeIndex 指针后移,如果到达数组末尾...
        takeIndex = 0; // ...则绕回数组开头(循环)
    count--; // 元素数量减少
    if (itrs != null)
        itrs.elementDequeued(); // 更新迭代器状态
    notFull.signal(); // 唤醒可能正在等待放入元素的生产者线程
    return x;
}

ArrayBlockingQueue总结

入队方法这个多个,究竟用哪个?通过上面的分析,不难发现,add()方法在队列满时抛出异常,对于日常的排队需求,很明显不符合要求;offer()方法相对add()方法友好一点,通过返回true或false表示插入结果,但是不会等待;通常很多排队场景是需要等待的,一直等待直到队列能插入元素为止,所以put()方法最符合日常需求,这也是为什么叫阻塞队列原因。

方法队列满时行为返回值
add(E e)抛出IllegalStateExceptionboolean
offer(E e)立即返回falseboolean
put(E e)阻塞直到队列有空位void
offer(E e, long timeout, TimeUnit unit)阻塞指定时间后返回falseboolean

同理,出队方法中具有阻塞特性的是take()方法,取元素时等待直到有元素可以取为止。

方法队列空时行为返回值
poll()立即返回nullE
take()阻塞直到队列有元素E
poll(long timeout, TimeUnit unit)阻塞指定时间后返回nullE

LinkedBlockingQueue核心特性与原理

LinkedBlockingQueue 是 Java 并发包中基于链表的阻塞队列实现,同样采用生产者-消费者模式,是处理多线程数据交换的重要工具。具有链表结构、容量灵活、线程安全与双锁设计、生产者-消费者模型等特性。

底层数据结构

/**
 * Linked list node class.
 */
static class Node<E> {
    E item;

    /**
     * One of:
     * - the real successor Node
     * - this Node, meaning the successor is head.next
     * - null, meaning there is no successor (this is the last node)
     */
    Node<E> next;

    Node(E x) { item = x; }
}

/** The capacity bound, or Integer.MAX_VALUE if none */
private final int capacity;

/** Current number of elements */
private final AtomicInteger count = new AtomicInteger();

/**
 * Head of linked list.
 * Invariant: head.item == null
 */
transient Node<E> head;

/**
 * Tail of linked list.
 * Invariant: last.next == null
 */
private transient Node<E> last;

Node类是单向链表‌的节点,存储队列元素‌;Node类型的head 和 last节点 分别指向链表首尾‌
原子计数器‌count 记录当前队列元素个数‌。

容量灵活

/**
 * Creates a {@code LinkedBlockingQueue} with a capacity of
 * {@link Integer#MAX_VALUE}.
 */
public LinkedBlockingQueue() {
    this(Integer.MAX_VALUE);
}

/**
 * Creates a {@code LinkedBlockingQueue} with the given (fixed) capacity.
 *
 * @param capacity the capacity of this queue
 * @throws IllegalArgumentException if {@code capacity} is not greater
 *         than zero
 */
public LinkedBlockingQueue(int capacity) {
    if (capacity <= 0) throw new IllegalArgumentException();
    this.capacity = capacity;
    last = head = new Node<E>(null);
}

创建LinkedBlockingQueue时,可以指定容量,不指定容量时默认为 Integer.MAX_VALUE,可视为无界队列‌。

线程安全与双锁设计

/** Lock held by take, poll, etc */
private final ReentrantLock takeLock = new ReentrantLock();

/** Wait queue for waiting takes */
@SuppressWarnings("serial") // Classes implementing Condition may be serializable.
private final Condition notEmpty = takeLock.newCondition();

/** Lock held by put, offer, etc */
private final ReentrantLock putLock = new ReentrantLock();

/** Wait queue for waiting puts */
@SuppressWarnings("serial") // Classes implementing Condition may be serializable.
private final Condition notFull = putLock.newCondition();

takeLock是取元素用的锁,putLock是插入元素用的锁。notEmpty:队列为空时,出队线程在此条件变量上等待‌;notFull:队列满时,入队线程在此条件变量上等待‌。

核心API方法

入队方法

和ArrayBlockingQueue一样,LinkedBlockingQueue同样提供了add/offer/put这几个方法,功能同ArrayBlockingQueue。我们截取其中的阻塞方法分析下:

/**
 * 将指定元素插入此队列的尾部,必要时等待空间可用。
 * 这是阻塞队列的核心方法之一,当队列满时会阻塞当前线程。
 *
 * @throws InterruptedException 如果在线程等待时被中断
 * @throws NullPointerException 如果指定的元素为 null
 */
public void put(E e) throws InterruptedException {
    // 参数校验:不允许插入 null 元素
    if (e == null) throw new NullPointerException();
    
    // 定义局部变量 c,用于记录插入前的队列元素数量
    final int c;
    
    // 创建新的节点包装要插入的元素
    final Node<E> node = new Node<E>(e);
    
    // 获取入队锁和原子计数器的本地引用,避免后续访问 this 指针
    final ReentrantLock putLock = this.putLock;
    final AtomicInteger count = this.count;
    
    // 获取入队锁,支持中断响应
    putLock.lockInterruptibly();
    try {
        /*
         * 注意:这里使用 count 作为等待条件判断,即使它没有被当前锁保护。
         * 这样是可行的,因为在持有 putLock 期间,count 只能减少(其他入队操作被锁阻塞),
         * 当 count 从容量值改变时,我们(或其他等待的入队线程)会被通知唤醒。
         * 同样适用于其他等待条件中使用 count 的情况。
         */
        
        // 循环检查队列是否已满(使用循环是为了防止虚假唤醒)
        while (count.get() == capacity) {
            // 队列满,当前线程在 notFull 条件上等待
            notFull.await();
        }
        
        // 队列有空闲空间,将新节点加入链表尾部
        enqueue(node);
        
        // 原子性地增加计数器,并返回增加前的值
        c = count.getAndIncrement();
        
        // 如果插入后队列仍未满,唤醒其他可能正在等待的入队线程
        if (c + 1 < capacity)
            notFull.signal();
    } finally {
        // 无论是否成功,最终都要释放锁
        putLock.unlock();
    }
    
    // 如果插入前队列为空(c == 0),需要唤醒可能正在等待的消费线程
    if (c == 0)
        signalNotEmpty();
}

/**
 * 将节点添加到队列尾部。
 * 该方法由持有putLock的生产者线程调用,确保线程安全。
 * 通过修改last指针实现O(1)时间复杂度操作。
 *
 * @param node 待添加的节点
 */
private void enqueue(Node<E> node) {
    // 调试断言:当前线程必须持有putLock
    // assert putLock.isHeldByCurrentThread();
    // 调试断言:队列原为空(last.next == null)
    // assert last.next == null;
    // 原子操作:将新节点链接到队列尾部
    last = last.next = node;
}

出队方法

和ArrayBlockingQueue一样,LinkedBlockingQueue同样提供了poll/remove/take这几个方法,功能同ArrayBlockingQueue。我们截取其中的阻塞方法分析下:

public E take() throws InterruptedException {
    // 定义局部变量:x用于存储出队元素,c用于记录出队前的队列元素数量
    final E x;
    final int c;
    
    // 获取原子计数器和出队锁的本地引用,优化性能
    final AtomicInteger count = this.count;
    final ReentrantLock takeLock = this.takeLock;
    
    // 获取出队锁,支持中断响应
    takeLock.lockInterruptibly();
    try {
        // 循环检查队列是否为空(使用循环防止虚假唤醒)
        while (count.get() == 0) {
            // 队列为空,当前线程在notEmpty条件上等待
            notEmpty.await();
        }
        
        // 队列非空,从链表头部移除元素
        x = dequeue();
        
        // 原子性地减少计数器,并返回减少前的值
        c = count.getAndDecrement();
        
        // 如果出队前队列中还有多于1个元素,唤醒其他可能正在等待的消费线程
        if (c > 1)
            notEmpty.signal();
    } finally {
        // 确保锁被释放,避免死锁
        takeLock.unlock();
    }
    
    // 如果出队前队列是满的(c == capacity),需要唤醒可能正在等待的生产线程
    if (c == capacity)
        signalNotFull();
    
    // 返回出队的元素
    return x;
}

ArrayBlockingQueue与LinkedBlockingQueue性能对比

ArrayBlockingQueue和LinkedBlockingQueue在并发性能上的差异主要源于它们的锁机制设计,这直接影响了生产者和消费者能否同时工作。

ArrayBlockingQueue‌ 使用‌单锁设计‌,生产者和消费者共用一把锁。这意味着在任何时刻,只能有一个线程执行入队或出队操作,两者无法并行。

LinkedBlockingQueue‌ 采用‌双锁设计‌,拥有独立的入队锁(putLock)和出队锁(takeLock)。这使得生产者和消费者可以‌真正并发执行‌,大幅提升吞吐量。

下面通过一个例子来对比两者的并发性能:创建相同数量的生产者和消费者线程,分别使用ArrayBlockingQueue和LinkedBlockingQueue,统计相同时间内的任务处理数量。

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicLong;

public class QueueBenchmark {
    private static final int PRODUCER_COUNT = 4;
    private static final int CONSUMER_COUNT = 4;
    private static final int TEST_DURATION_SECONDS = 10;
    private static final int QUEUE_CAPACITY = 1000;

    private static class Producer implements Runnable {
        private final BlockingQueue<Integer> queue;
        private final AtomicLong counter;
        private volatile boolean running = true;

        public Producer(BlockingQueue<Integer> queue, AtomicLong counter) {
            this.queue = queue;
            this.counter = counter;
        }

        @Override
        public void run() {
            try {
                while (running) {
                    queue.put(1);
                    counter.incrementAndGet();
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }

        public void stop() {
            running = false;
        }
    }

    private static class Consumer implements Runnable {
        private final BlockingQueue<Integer> queue;
        private final AtomicLong counter;
        private volatile boolean running = true;

        public Consumer(BlockingQueue<Integer> queue, AtomicLong counter) {
            this.queue = queue;
            this.counter = counter;
        }

        @Override
        public void run() {
            try {
                while (running) {
                    queue.take();
                    counter.incrementAndGet();
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }

        public void stop() {
            running = false;
        }
    }

    public static long benchmarkQueue(BlockingQueue<Integer> queue, String queueType)
            throws InterruptedException {

        AtomicLong produceCounter = new AtomicLong(0);
        AtomicLong consumeCounter = new AtomicLong(0);

        Producer[] producers = new Producer[PRODUCER_COUNT];
        Consumer[] consumers = new Consumer[CONSUMER_COUNT];

        Thread[] producerThreads = new Thread[PRODUCER_COUNT];
        Thread[] consumerThreads = new Thread[CONSUMER_COUNT];

        // 创建并启动生产者线程
        for (int i = 0; i < PRODUCER_COUNT; i++) {
            producers[i] = new Producer(queue, produceCounter);
            producerThreads[i] = new Thread(producers[i], "Producer-" + queueType + "-" + i);
            producerThreads[i].start();
        }

        // 创建并启动消费者线程
        for (int i = 0; i < CONSUMER_COUNT; i++) {
            consumers[i] = new Consumer(queue, consumeCounter);
            consumerThreads[i] = new Thread(consumers[i], "Consumer-" + queueType + "-" + i);
            consumerThreads[i].start();
        }

        // 运行测试指定时间
        Thread.sleep(TEST_DURATION_SECONDS * 1000);

        // 停止所有线程
        for (Producer producer : producers) {
            producer.stop();
        }
        for (Consumer consumer : consumers) {
            consumer.stop();
        }

        // 中断线程以确保快速停止
        for (Thread thread : producerThreads) {
            thread.interrupt();
        }
        for (Thread thread : consumerThreads) {
            thread.interrupt();
        }

        // 等待所有线程结束
        for (Thread thread : producerThreads) {
            thread.join(1000);
        }
        for (Thread thread : consumerThreads) {
            thread.join(1000);
        }

        long totalOperations = produceCounter.get() + consumeCounter.get();
        System.out.printf("%s 性能结果:%n", queueType);
        System.out.printf("  生产操作数: %,d%n", produceCounter.get());
        System.out.printf("  消费操作数: %,d%n", consumeCounter.get());
        System.out.printf("  总操作数: %,d%n", totalOperations);
        System.out.printf("  吞吐量: %,d 操作/秒%n", totalOperations / TEST_DURATION_SECONDS);
        System.out.println();

        return totalOperations;
    }

    public static void main(String[] args) throws InterruptedException {
        System.out.println("开始队列性能对比测试...");
        System.out.printf("测试配置: %d 生产者, %d 消费者, %d 秒测试时间, 队列容量 %d%n%n",
                PRODUCER_COUNT, CONSUMER_COUNT, TEST_DURATION_SECONDS, QUEUE_CAPACITY);

        // 测试 ArrayBlockingQueue
        BlockingQueue<Integer> arrayQueue = new ArrayBlockingQueue<>(QUEUE_CAPACITY);
        long arrayOps = benchmarkQueue(arrayQueue, "ArrayBlockingQueue");

        // 测试 LinkedBlockingQueue
        BlockingQueue<Integer> linkedQueue = new LinkedBlockingQueue<>(QUEUE_CAPACITY);
        long linkedOps = benchmarkQueue(linkedQueue, "LinkedBlockingQueue");

        // 性能对比分析
        double performanceRatio = (double) linkedOps / arrayOps;
        System.out.println("性能对比分析:");
        System.out.printf("LinkedBlockingQueue 比 ArrayBlockingQueue 快 %.2f 倍%n", performanceRatio);

        if (performanceRatio > 1.0) {
            System.out.println("结论: LinkedBlockingQueue 在并发场景下展现出明显的吞吐量优势");
        } else {
            System.out.println("结论: 在当前测试条件下,两种队列性能相近");
        }
    }
}

运行结果:

开始队列性能对比测试...
测试配置: 4 生产者, 4 消费者, 10 秒测试时间, 队列容量 1000

ArrayBlockingQueue 性能结果:
  生产操作数: 104,607,461
  消费操作数: 104,607,461
  总操作数: 209,214,922
  吞吐量: 20,921,492 操作/秒

LinkedBlockingQueue 性能结果:
  生产操作数: 60,179,525
  消费操作数: 60,179,524
  总操作数: 120,359,049
  吞吐量: 12,035,904 操作/秒

性能对比分析:
LinkedBlockingQueue 比 ArrayBlockingQueue 快 0.58 倍
结论: 在当前测试条件下,两种队列性能相近

从运行结果看,线程数量比较少的情况下,ArrayBlockingQueue的性能反而比LinkedBlockingQueue的好。加大生产消费线程的数量到10,再次运行程序:

开始队列性能对比测试...
测试配置: 10 生产者, 10 消费者, 10 秒测试时间, 队列容量 1000

ArrayBlockingQueue 性能结果:
  生产操作数: 56,185,094
  消费操作数: 56,185,094
  总操作数: 112,370,188
  吞吐量: 11,237,018 操作/秒

LinkedBlockingQueue 性能结果:
  生产操作数: 56,596,683
  消费操作数: 56,595,693
  总操作数: 113,192,376
  吞吐量: 11,319,237 操作/秒

性能对比分析:
LinkedBlockingQueue 比 ArrayBlockingQueue 快 1.01 倍
结论: LinkedBlockingQueue 在并发场景下展现出明显的吞吐量优势

加大生产消费线程的数量到20,再次运行程序:

开始队列性能对比测试...
测试配置: 20 生产者, 20 消费者, 10 秒测试时间, 队列容量 1000

ArrayBlockingQueue 性能结果:
  生产操作数: 33,684,938
  消费操作数: 33,684,938
  总操作数: 67,369,876
  吞吐量: 6,736,987 操作/秒

LinkedBlockingQueue 性能结果:
  生产操作数: 57,188,358
  消费操作数: 57,187,361
  总操作数: 114,375,719
  吞吐量: 11,437,571 操作/秒

性能对比分析:
LinkedBlockingQueue 比 ArrayBlockingQueue 快 1.70 倍
结论: LinkedBlockingQueue 在并发场景下展现出明显的吞吐量优势

从运行结果可以看出,随着并发线程数的增多,LinkedBlockingQueue的性能优势越明显。

java并发编程(三)客户端加锁与组合 客户端加锁: java为线程安全提供了一套安全的集合类操作 在java.util.Collections中,在多线程下能 安全的CRUD,但是应该注意,仅是对几何类进行同步,下面看一个实例。 若没有则添加实例: public class ListHelper { public List list = Collections.synchronizedList(new ArrayLi 阅读详情

相关推荐

Unity调用手机摄像头避坑指南:从权限检测到自动对焦问题解决

本文深入解析了Unity移动端集成手机摄像头时遇到的权限、对焦与性能等核心难题。针对权限管理,提供了健壮的原生接口调用方案;针对自动对焦模糊问题,详细对比了原生插件与第三方插件两种解决路径;并分享了分辨率选择、数据处理及功耗控制等关键性能优化技巧,帮助开发者构建稳定高效的摄像头功能。

weixin_29007243的博客 470

Java 阻塞队列(BlockingQueue)实战与原理详解

Java 阻塞队列(BlockingQueue)实战与原理详解

m0_54258715的博客 1989

Unity中URP下的菲涅尔效果实现(个性化修改)

在上篇文章中,我们实现了 URP 下的菲涅尔基础效果,我们在这篇文章中,对其进行自定义化修改。

楠溪泽岸的博客 2063

ArrayBlockingQueue 浅谈

是包下的有界、阻塞、FIFO(先进先出)队列,是 Java 并发编程中最核心的阻塞队列实现之一,底层基于定长数组实现,所有核心操作都保证线程安全。有界可控,安全可靠:容量固定,能精准控制队列的内存占用,不会因为无限入队导致内存溢出(OOM),这是生产环境的核心优势;性能优异:底层是数组,存取速度快,循环数组无内存碎片,无扩容开销;锁设计高效:一把独占锁 + 两个 Condition,精准唤醒,无锁竞争的额外开销,线程唤醒无冗余;线程安全:所有方法都加锁保护,无需手动处理并发安全问题;支持公平性。

萧曳丶 700

SpringCloud2025+SpringBoot3.5.0+gateway+webflux子服务路由报503

本来昨天就应该也记录下,免得忘记的,但是有点晚了,酒没写,真的是被坑惨了。当然这也是追求最新的代价,也是对新技术、老知识点的重温。依赖更粒度让架构的知识面更高,需要非常清楚另外说不定是有新的负载均衡要出现了,就想当初loadbalancer取代Ribbon一样好了,真是坑了半夜,昨晚都没有写,今天才复盘,希望能帮到大家,让更多在坑里的技术人爬出来。

zwrlj527的专栏 1998

保姆级教程:用rosbag录制和回放你的第一个ROS机器人数据(附C++/Python代码)

本文提供了一份详细的ROS机器人数据录制与回放教程,涵盖从命令行操作到C++/Python编程实现的全流程。通过rosbag工具,开发者可以高效记录和回放机器人运行时的传感器数据和控制指令,适用于故障复现、算法调试等多种场景。教程还包含高级技巧如时间同步和大文件处理,帮助用户快速掌握ROS数据管理核心技能。

weixin_29313239的博客 283

java ArrayBlockingQueue

ArrayBlockingQueue介绍 ArrayBlockingQueue是数组实现的线程安全的有界的阻塞队列。 线程安全是指,ArrayBlockingQueue内部通过“互斥锁”保护竞争资源,实现了多线程对竞争资源的互斥访问。而有界,则是指ArrayBlockingQueue对应的数组是有界限的。 阻塞队列,是指多线程访问竞争资源时,当竞争资源已被某线程获取时,其它要获取该资源

Code-lover's Learning Notes 2411

宇树机器人丝滑的二人转动作背后,MPC到底是什么?

了解MPC,让我们闻“机”起舞。

Barok的博客 2144

ArrayBlockingQueue<E> 笔记

ArrayBlockingQueue是一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。队列的头部 是在队列中存在时间最长的元素。队列的尾部是在队列中存在时间最短的元素。新元素插入到队列的尾部,队列获取操作则是从队列头部开始获得元素。这是一个典型的“有界缓存区”,固定大小的数组在其中保持生产者插入的元素和使用者提取的元素。一旦创建了这样的缓存区,就不能再增加其容量。试...

changgongcaomu的专栏 356

Python爬虫:爬取健身平台运动数据(跑步、骑行轨迹)

通过分析这些运动数据,用户能够了解自己的运动趋势、运动成绩,并作出相应的调整。通过使用Strava的API,我们成功地获取了用户的运动活动信息和运动轨迹数据,并进行了存储与可视化。Strava的API提供了丰富的数据接口,能够通过OAuth2认证获取用户的运动数据,包括历史数据、运动轨迹等信息。Strava的API还提供了获取活动轨迹数据的接口,返回的数据格式通常是GeoJSON格式,适用于地图可视化。随着大数据技术的发展,运动数据的分析和可视化为个人健康管理、运动训练计划的优化等提供了强大的支持。

2201_76125261的博客 1074

龙芯2K1000 OTG双模驱动深度解析

是Synopsys公司开发的USB 2.0 OTG控制器IP核,龙芯2K1000内置该IP,作为唯一的OTG控制器(PCI Dev4 Func0),物理基地址0x40000000。核心能力:同时支持Host主机模式(接U盘/键盘)、Device设备模式(虚拟串口ttyGS0)、OTG双模自动切换速率驱动路径:内核源码PMON:启用otg0设备树内核:开启HOSTPERIPHERALG_SERIAL启动参数脚本:sysfs角色切换+开机自启Host按以上配置,龙芯2K1000 DWC2 OTG可完美实现。

weixin_44932366的博客 375

LinkedBlockingQueue

一、定义 LinkedBlockingQueue继承自AbstractQueue,实现了BlockingQueue,Serializable接口。 LinkedBlockingQueue是一个基于已链接节点的,范围任意的blocking queue 此队列按FIFO(先进先出)排序元素 新元素插入到队列的尾部,并且队列获取操作会获得位于队列头部的元素 链接队列的吞吐量通常要高于基于数组的对列(ArrayBlockingQueue),但是在大多数并发应用程序中,其可预知的性能要低 可选的容量范围

雪雪 3万+

Gmsh+OCC对应版本库编译

输入 GMSH_DLL_EXPORT值C:\LIB\OCCT-7_8_0-i;9、如果只是生成库文件编译gmesh工程即可生成文件在C:\LIB\Gmsh\gmsh-4.13.1-Windows64-B\Debug 下gmsh.dll、gmsh.lib、gmsh.h、gmshc.h文件;3、CMAK编译Gmsh对应OCC版本的,CMAK选择源文件路径C:\LIB\Gmsh\gmsh-4.13.1-source,生成构建文件路径C:\LIB\Gmsh\gmsh-4.13.1-Windows64-B。

u013401364的博客 411

【JUC系列-11】深入理解LinkedBlockingQueue的底层实现

深入理解LinkedBlockingQueue的底层原理和基本使用

zhenghuishengq的博客 6216

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

ArrayBlockingQueue与LinkedBlockingQueue

BlockingQueue 阻塞队列 先把这个接口的几个方法介绍下 public interface BlockingQueue<E> extends Queue<E> { //将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量) //在成功时返回 true,如果此队列已满,则抛IllegalStateException。 boolean add(E e); //将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量) // 将指定的

u010215318的博客 500

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

LinkedBlockingQueue - 秒懂 - 图解

疯狂创客圈 经典图书 : 《Netty Zookeeper Redis 高并发实战》 面试必备 + 面试必备 + 面试必备 【博客园总入口 】 疯狂创客圈 经典图书 : 《SpringCloud、Nginx高并发核心编程》 大厂必备 + 大厂必备 + 大厂必备 【博客园总入口 】 入大厂+涨工资必备: 高并发【 亿级流量IM实战】 实战系列 【 SpringCloud Nginx秒杀】 实战系列 【博客园总入口 】 说明:阅读本文之前,请先掌握本文前置知识:..

架构师尼恩 1万+

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

深入探究LinkedBlockingQueue、ArrayBlockingQueue、PriorityBlockingQueue、DelayQueue源码

深入探究LinkedBlockingQueue、ArrayBlockingQueue、PriorityBlockingQueue、DelayQueue源码

沐雨橙风ιε的博客 539

Java 并发编程全解:锁、并发容器、阻塞队列与异步编程

Java并发编程核心组件深度解析 本文系统梳理了Java并发编程的四大核心模块:1)ReentrantLock显式锁机制,详解其可重入、可中断、公平锁等特性及AQS实现原理;2)ConcurrentHashMap的架构演进,从JDK7分段锁到JDK8的CAS+synchronized优化;3)BlockingQueue阻塞队列体系,分析Array/Linked/Synchronous/DelayQueue的实现差异与适用场景;4)CompletableFuture异步编程,展示链式调用、任务组合等函数式编程

2504_94294476的博客 3021

Java ArrayBlockingQueue:高并发队列原理与应用

在当今的软件开发领域,高并发已经成为一个不可忽视的关键问题。尤其是在处理大量数据和高频请求的场景下,如何有效地管理并发访问和同步操作,成为了提升系统性能和稳定性的关键。Java作为一门广泛应用于企业级应用开发的语言,提供了丰富的并发工具和类库。其中,ArrayBlockingQueue作为一种线程安全的队列实现,在Java并发编程中扮演着重要角色。它不仅能够有效地解决多线程之间的数据同步问题,还能在保证线程安全的同时,提高程序的执行效率。

MoneyHacksPro的博客 980

深度解析阻塞队列ArrayBlockingQueue

前言 前一篇 深度解析阻塞队列LinkedBlockingQueue 从底层源码和结构原理入手,较为深入的讲解了由单链表实现的阻塞队列LinkedBlockingQueue。本篇所要讲解的依然是阻塞队列,阻塞还是那个阻塞,队列还是那个队列。只是ArrayBlockingQueue使用数组实现的阻塞队列。 由于前一篇已经重点讲解了阻塞队列中,阻塞API的实现。所以本篇的重点放在ArrayBlocki...

Sicimike的博客 2202

用Java如何设计一个阻塞队列,然后说说ArrayBlockingQueue和LinkedBlockingQueue

前言 用Java如何设计一个阻塞队列,这个问题是在面滴滴的时候被问到的。当时确实没回答好,只是说了用个List,然后消费者再用个死循环一直去监控list的是否有值,有值的话就处理List里面的内容。回头想想,自己真是一个大傻X,也只有我才会这么设计一个阻塞队列(再说,我这也不是阻塞的队列)。 结果自己面试完之后,也没去总结这部分知识,然后过了一段时间,某教育机构的面试又被问到类似的问题了,只不过是换了一个形式,“请用wait方法和notify方法实现一套有生产者和消费者的这种逻辑”。然后我就又蒙圈了,追悔莫

Jimoer 1588

Java并发编程:阻塞队列LinkedBlockingQueue

Java并发编程:阻塞队列(一)ArrayBlockingQueue 我们都知道ArrayList和LinkedLsit的区别,其实LinkedBlockingQueue和ArrayBlockingQueue之间也存在着类似的区别。因为它们都显现接口BlockingQueue并继承自Queue接口,所以LinkedBlockingQueue和ArrayBlockingQueue的API几乎是一样...

Jodness' Blogs 630

[源码解析]无界阻塞队列:LinkedBlockingQueue

本篇文章是我解析Java-API的笔记,更多的是从方法着手,少对类的整体情况进行说明,故并不详尽,可供大家参考。 如果文中阐述不全或不对的,多多交流。

进步*于辰的博客 1万+

ArrayBlockingQueue、PriorityBlockingQueue、LinkedBlockingQueue的使用

`ArrayBlockingQueue` 是一个先进先出的有界队列,初始化容量后,无法进行修改 api add 超出队列会报错 java.lang.IllegalStateException: Queue full (对应的remove 方法) offer 添加返回true 和 false put 会阻塞 (对应的take) poll 拿不出来,返回null peek 只拿出第一位,不会删...

renguiriyue的博客 477

【阻塞队列】- LinkedBlockingQueue 的原理

上一篇文章已经介绍了 ArrayBlockingQueue 的迭代器原理,那么现在这篇文章就要介绍 LinkedBlockingQueue 了,这个阻塞队列和 ArrayBlockingQueue 的唯一区别就是 ArrayBlockingQueue 是数组存储,LinkedBlockingQueue 是链表存储。【阻塞队列】- 生产者和消费者模式【阻塞队列】- ArrayBlockingQueue 的原理【阻塞队列】- ArrayBlockingQueue 的原理-迭代器。

Jwasx的博客 1169

线程池任务队列选型指南(ArrayBlockingQueue vs LinkedBlockingQueue 原理与性能对比)

掌握线程池的任务队列选型关键,对比ArrayBlockingQueue与LinkedBlockingQueue的实现原理与性能差异。分析阻塞机制、内存占用及适用场景,助你优化线程池配置,提升系统吞吐量,值得收藏。

ProceChat的博客 420
上一篇: 文件系统监控利器:Java NIO WatchService深度解析与实践指南
下一篇: 开发避坑指南(73):itext7 pdf表单字体加粗解决方案
帧栈
博客等级 码龄2年 335粉丝 120原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

帧栈

您的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值