实现ArrayBlockingQueue-阻塞队列

本文深入探讨了队列的基本特性和实现原理,包括先进先出原则、写入和消费队列时的阻塞机制,以及如何使用Java实现一个简单的队列。通过分析JDK源码,进一步讲解了ArrayBlockingQueue的具体实现,包括锁机制、条件变量和队列操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一:基本特性

二:实现原理

三:实现代码

一:基本特性

    先进先出、写入队列空间不足时会阻塞、获取队列数据是队列为空会阻塞

二:实现原理

    初始化队列、写入队列、消费队列

初始化队列

private String[] items = new String[5];
private Object full = new Object();
private Object empty = new Object();
private static int count;
private static int putIndex = 0;
private static int getIndex = 0;

写入队列

/**
	 * 写入锁put
	 */
	public void put(String t) {
		synchronized (full) {
			while (count == items.length) {
				try {
					full.wait();
				} catch (InterruptedException e) {
					break;
				}
			}
		}
		synchronized (empty) {
			items[putIndex] = t;
			putIndex++;

			count++;
			if (putIndex == items.length) {
				putIndex = 0;
			}
			empty.notify();
		}

	}

消费队列

/**
	 * 消费锁
	 * @return
	 */
	public String get() {
		synchronized (empty) {
			while (count == 0) {
				try {
					empty.wait();
				} catch (InterruptedException e) {
					return null;
				}
			}
		}

		synchronized (full) {
			Object result = items[getIndex];
			items[getIndex] = null;

			getIndex++;
			count--;
			if (getIndex == items.length) {
				getIndex = 0;
			}

			full.notify();
			return (String) result;
		}
	}

这样就可以实现一个队列先进先出的功能。

原理很简单:写入队列,在写入队列是先判断队列中元素是否已满,如果满则进入等待状态;消费队列,在消费前先看下队列中是否有元素,如果有就消费,没有进入等待。和消费者生产者模式类似。

三:实现代码:

我们了解了基本的原理,现在看下jdk是如何实现的

package com.block;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
 * 重写ArrayBlockingQueue<E>
 * @author l
 *
 * @param <E>
 */
public class ArrayQueueryTest3<E> {

	private final E[] items;//数组
	private final ReentrantLock lock;//锁
	private final Condition notEmpty;//
	private final Condition notFull;//
	private int count;//总数
	private int takeIndex;//
	private int putIndex;//
	
	
	public ArrayQueueryTest3(int capacity){
		this(capacity,false);
	}
	
	public ArrayQueueryTest3(int capacity,boolean fair){
		if(capacity <= 0)
			throw new IllegalArgumentException();
		this.items = (E[]) new Object[capacity];
		lock = new ReentrantLock(fair);
		notEmpty = lock.newCondition();
		notFull = lock.newCondition();
	}
	
	public void put(E e) throws InterruptedException{
		if(e == null) throw new NullPointerException();
		final E[] items = this.items;
		final ReentrantLock lock = this.lock;
		lock.lockInterruptibly();
		
		try {
			try {
				while(count == items.length){
					notFull.await();//等待
				}
			} catch (Exception e2) {
				notFull.signal();
				throw e2;
			}
			insert(e);//插入队列
		} finally{
			lock.unlock();
		}
	}
	private void insert(E x){
		items[putIndex] = x;
		putIndex = inc(putIndex);
		++count;
		notEmpty.signal();
	}
	
	public E take() throws InterruptedException {
		final ReentrantLock lock = this.lock;
		lock.lockInterruptibly();
		try{
			try{
				while(count == 0)
					notEmpty.await();//等待
			}catch(InterruptedException ie){
				notEmpty.signal();
				throw ie;
			}
			E x = extract();
			return x;
		}finally{
			lock.unlock();
		}
	}
	
	public E poll(){
		final ReentrantLock lock = this.lock;
		lock.lock();
		try {
			if(count == 0)
				return null;
			E x = extract();
			return x;
		} finally {
			lock.unlock();
		}
	}
	
	private E extract(){
		final E[] items = this.items;
		E x = items[takeIndex];
		items[takeIndex] = null;
		takeIndex = inc(takeIndex);
		--count;
		notFull.signal();
		return x;
	}
	
	final int inc(int i){
		return (++i == items.length)? 0: i;
	}
	
	private boolean isEmpty(){
		final ReentrantLock lock = this.lock;
		lock.lock();
		try{
			return items.length == 0;
		}finally{
			lock.unlock();
		}
	}
	
	public int size(){
		final ReentrantLock lock = this.lock;
		lock.lock();
		try{
			return count;
		}finally{
			lock.unlock();
		}
	}
	
	public E poll(long timeout, TimeUnit unit) throws Exception{
		long nanos = unit.toNanos(timeout);
		final ReentrantLock lock = this.lock;
		lock.lockInterruptibly();
		try {
			for (;;) {
				if(count != 0){
					E x = extract();
					return x;
				}
				if(nanos <= 0)
					return null;
				try {
					nanos = notEmpty.awaitNanos(nanos);//等待时长
				} catch (Exception e) {
					notEmpty.signal();
					throw e;
				}
			}
		} finally {
			lock.unlock();
		}
	}
	
	public static void main(String[] args) throws Exception {
		ArrayQueueryTest3<String> at = new ArrayQueueryTest3<>(4, false);
		at.put("123");
		at.put("123");
		at.put("123");
		System.out.println(at.size());
		while(!at.isEmpty())
		//如果生产者没有数据,则会一直进入等待状态,即使生产出数据,依旧会处在等待状态
			System.out.println(at.take());
		//每隔3s会进行一次get即使没有也会去get,当生产数据后可以依旧可以正常执行
			System.out.println(at.poll(3000,TimeUnit.MILLISECONDS));
		
	}
}

 

转载于:https://my.oschina.net/869088067/blog/3037089

### 阻塞队列的数据结构及其用法 #### 1. 阻塞队列的核心概念 阻塞队列是一种特殊的队列,它支持在队列入口和出口处的阻塞操作。如果队列为空,则尝试移除元素的操作会被阻塞;如果队列为满,则尝试插入新元素的操作也会被阻塞[^1]。 #### 2. Java 中的标准实现 Java 提供了 `java.util.concurrent.BlockingQueue` 接口来定义阻塞队列的行为,并提供了多种具体实现类: - **ArrayBlockingQueue**: 基于固定大小数组的有界阻塞队列,遵循 FIFO(先进先出)原则[^3]。 - **LinkedBlockingQueue**: 基于链表的可选限界阻塞队列,默认情况下无界。 - **PriorityBlockingQueue**: 支持优先级排序的无界阻塞队列,允许按照自然顺序或自定义比较器进行排序。 - **SynchronousQueue**: 特殊类型的阻塞队列,不保存任何元素,仅用于传递数据。 - **DelayedWorkQueue**: 一种延迟队列,只有当元素达到其设定的时间阈值时才能被取出。 #### 3. C++ 的简单实现 虽然 C++ 标准库未提供内置的阻塞队列,但可以通过组合 `std::queue` 和互斥锁手动构建一个简单的阻塞队列。以下是其实现示例: ```cpp #include <queue> #include <mutex> #include <condition_variable> template<typename T> class BlockingQueue { private: std::queue<T> queue_; mutable std::mutex mutex_; std::condition_variable cond_; public: void push(T value) { std::lock_guard<std::mutex> lock(mutex_); queue_.push(value); cond_.notify_one(); // 唤醒等待线程 } T pop() { std::unique_lock<std::mutex> lock(mutex_); while (queue_.empty()) { // 如果队列为空则阻塞当前线程 cond_.wait(lock); } T result = queue_.front(); queue_.pop(); return result; } bool empty() const { std::lock_guard<std::mutex> lock(mutex_); return queue_.empty(); } }; ``` 上述代码通过条件变量实现了基本的阻塞功能,在多线程环境下能够安全地管理资源访问。 #### 4. 应用场景分析 阻塞队列广泛应用于并发编程领域,特别是在生产者-消费者模式中表现尤为突出。在这种模式下,多个生产者向共享队列添加任务项,而多个消费者从中提取并处理这些任务项[^4]。这种设计不仅提高了系统的吞吐量,还降低了开发复杂度。 例如,在 Web 服务后台任务调度系统中,可以利用阻塞队列缓存待执行的任务请求,由专门的工作线程池逐一完成计算密集型作业[^2]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值