2.工作队列

work queues

为什么会出现工作队列?
Simple队列是一一对应的,而且我们实际开发,生产者发送消息是毫无费力的,而消费者一般是要跟业务相结合的,消费者接收到消息之后就需要处理,可能需要花费时间,这时候队列就会挤压了很多消息.

1.轮询分发     
消费者1和消费者2处理的消息是一样多的.
消费者1:偶数,消费者2:奇数
这种方式叫做轮询分发(round-robin),任务总是均分.不管谁忙谁清闲.

2.公平分发
消费者2比消费者1多处理.
能者多劳

项目搭建(轮询分发(round-robin))

package com.mmr.rabbit.work;

import com.mmr.rabbit.util.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

public class Send {
	
	private static final String QUEUE_NAME="test_work_queue";
	
	public static void main(String[] args) throws Exception {
		//获取连接
		Connection connection = ConnectionUtils.getConnection();
		//获取channel
		Channel channel = connection.createChannel();
		//声明队列
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		for (int i = 0; i < 50; i++) {
			String msg = "hello"+i;
			channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
			System.err.println("WQ:"+msg);
			Thread.sleep(i*20);
		}
		channel.close();
		connection.close();
	}
	
}


package com.mmr.rabbit.work;

import java.io.IOException;

import com.mmr.rabbit.util.ConnectionUtils;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

public class Recv1 {
	
	private static final String QUEUE_NAME="test_work_queue";
	
	public static void main(String[] args) throws Exception {
		//获取连接
		Connection connection = ConnectionUtils.getConnection();
		//获取channel
		Channel channel = connection.createChannel();
		//声明队列
		channel.queueDeclare(QUEUE_NAME,false , false, false, null);
		//定义一个消费者
		Consumer consumer = new DefaultConsumer(channel){
			  @Override
			  public void handleDelivery(String consumerTag, Envelope envelope,AMQP.BasicProperties properties, byte[] body)
			      throws IOException {
			    String message = new String(body, "UTF-8");
			    System.err.println("[1] Recv:" + message + "");
			    try{
			    	Thread.sleep(2000);
			    }catch(Exception e){
			    	e.printStackTrace();
			    }finally{
			    	System.err.println("[1] done");
			    }
			  }
		};
		boolean autoAck = true;
		channel.basicConsume(QUEUE_NAME,autoAck,consumer);
	}
	
}


package com.mmr.rabbit.work;

import java.io.IOException;

import com.mmr.rabbit.util.ConnectionUtils;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

public class Recv2 {
	
	private static final String QUEUE_NAME="test_work_queue";
	
	public static void main(String[] args) throws Exception {
		//获取连接
		Connection connection = ConnectionUtils.getConnection();
		//获取channel
		Channel channel = connection.createChannel();
		//声明队列
		channel.queueDeclare(QUEUE_NAME,false , false, false, null);
		//定义一个消费者
		Consumer consumer = new DefaultConsumer(channel){
			  @Override
			  public void handleDelivery(String consumerTag, Envelope envelope,AMQP.BasicProperties properties, byte[] body)
			      throws IOException {
			    String message = new String(body, "UTF-8");
			    System.err.println("[2] Recv:" + message + "");
			    try{
			    	Thread.sleep(1000);
			    }catch(Exception e){
			    	e.printStackTrace();
			    }finally{
			    	System.err.println("[2] done");
			    }
			  }
		};
		boolean autoAck = true;
		channel.basicConsume(QUEUE_NAME,autoAck,consumer);
	}
	
}

项目搭建(公平分发(fair disaptch))

使用公平分发,必须关系自动应答ack.改为手动

package com.mmr.rabbit.workfair;

import com.mmr.rabbit.util.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

public class Send {
	
	private static final String QUEUE_NAME="test_work_queue";
	
	public static void main(String[] args) throws Exception {
		//获取连接
		Connection connection = ConnectionUtils.getConnection();
		//获取channel
		Channel channel = connection.createChannel();
		//声明队列
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		//每个消费者发送确认消息之前,消息队列不发送下一个消息到消费者,一次只处理一个消息.
		int prefetchCount = 1;
		channel.basicQos(prefetchCount);
		
		for (int i = 0; i < 50; i++) {
			String msg = "hello"+i;
			channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
			System.err.println("WQ:"+msg);
			Thread.sleep(i*20);
		}
		channel.close();
		connection.close();
	}
	
}


package com.mmr.rabbit.workfair;

import java.io.IOException;

import com.mmr.rabbit.util.ConnectionUtils;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

public class Recv1 {
	
	private static final String QUEUE_NAME="test_work_queue";
	
	public static void main(String[] args) throws Exception {
		//获取连接
		Connection connection = ConnectionUtils.getConnection();
		//获取channel
		final Channel channel = connection.createChannel();
		//声明队列
		channel.queueDeclare(QUEUE_NAME,false , false, false, null);
		//保证每次只分发一个
		channel.basicQos(1);
		
		//定义一个消费者
		Consumer consumer = new DefaultConsumer(channel){
			  @Override
			  public void handleDelivery(String consumerTag, Envelope envelope,AMQP.BasicProperties properties, byte[] body)
			      throws IOException {
			    String message = new String(body, "UTF-8");
			    System.err.println("[1] Recv:" + message + "");
			    try{
			    	Thread.sleep(2000);
			    }catch(Exception e){
			    	e.printStackTrace();
			    }finally{
			    	System.err.println("[1] done");
			    	channel.basicAck(envelope.getDeliveryTag(), false);
			    }
			  }
		};
		boolean autoAck = false;//自动应答
		channel.basicConsume(QUEUE_NAME,autoAck,consumer);
	}
	
}


package com.mmr.rabbit.workfair;

import java.io.IOException;

import com.mmr.rabbit.util.ConnectionUtils;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

public class Recv2 {
	
	private static final String QUEUE_NAME="test_work_queue";
	
	public static void main(String[] args) throws Exception {
		//获取连接
		Connection connection = ConnectionUtils.getConnection();
		//获取channel
		final Channel channel = connection.createChannel();
		//声明队列
		channel.queueDeclare(QUEUE_NAME,false , false, false, null);
		
		channel.basicQos(1);
		
		//定义一个消费者
		Consumer consumer = new DefaultConsumer(channel){
			  @Override
			  public void handleDelivery(String consumerTag, Envelope envelope,AMQP.BasicProperties properties, byte[] body)
			      throws IOException {
			    String message = new String(body, "UTF-8");
			    System.err.println("[2] Recv:" + message + "");
			    try{
			    	Thread.sleep(1000);
			    }catch(Exception e){
			    	e.printStackTrace();
			    }finally{
			    	System.err.println("[2] done");
			    	channel.basicAck(envelope.getDeliveryTag(),false);
			    }
			  }
		};
		boolean autoAck = false;
		channel.basicConsume(QUEUE_NAME,autoAck,consumer);
	}
	
}

<think>好的,我现在需要回答用户关于队列数据结构的问题,包括其特点、工作原理、应用以及优缺点。首先,我得回忆队列的基本概念。队列是一种先进先出(FIFO)的数据结构,和栈的后进先出不同。队列的基本操作包括入队(enqueue)和出队(dequeue),通常还有查看队首元素等操作。 接下来是特点。队列的主要特点就是FIFO,确保最先进入的元素最先被处理。存储方式上,队列可以用数组或链表实现。动态数组实现的队列可能需要处理扩容问题,而链表实现则更灵活,但可能有额外的内存开销。此外,队列还有不同的类型,比如循环队列、阻塞队列、优先队列等,这些变种有各自的应用场景。 工作原理方面,需要解释入队和出队的过程。比如,在链表实现中,入队是在尾部添加节点,出队是在头部移除节点。如果是数组实现,可能需要维护front和rear指针,并处理队列满或空的条件,循环队列可以更高效地利用数组空间。 应用场景的话,队列常用于需要顺序处理的场景。比如任务调度、缓冲处理、广度优先搜索(BFS)等。在开发中,阻塞队列用于多线程协调,生产者-消费者模型就是一个典型例子。优先队列则用于需要按优先级处理的场景,比如任务管理系统。 优势方面,队列的顺序性保证了公平性,结构简单易于实现,解耦生产者和消费者,缓冲作用在高并发下很重要。而劣势可能包括性能问题,比如链表实现的节点内存开销,数组实现的扩容成本。普通队列功能有限,需要特定场景使用变种队列。 现在需要确保回答符合用户给的格式要求:使用中文,行内公式用$...$,独立公式用$$...$$。不过目前内容中可能涉及公式的地方不多,但如果有的话要注意格式。比如提到队列的时间复杂度,可以写成$O(1)$。另外,需要引用用户提供的引用资料,特别是引用[2]和[3]中的内容。 检查引用部分,引用[2]提到链表实现队列的优势,比如动态扩容和边界处理,而引用[3]涉及阻塞队列的应用。在回答中需要自然地插入这些引用,比如在讨论队列实现方式时提到链表的选择,或者在阻塞队列部分引用[3]。 然后生成相关问题,可能需要根据回答内容提出几个问题,比如队列的不同实现方式比较,阻塞队列的应用,或者队列在BFS中的作用等。 现在组织内容,确保结构清晰,分点回答每个部分。先介绍队列的基本概念,然后特点、原理、应用,最后优缺点。每个部分用标题分开,使用中文,避免使用Markdown格式,但用户给的示例中用了加粗,这里可能需要注意。不过根据用户指令,用户可见层需要结构清晰,逐步解决问题,所以可能需要用序号或分段。 最后检查LaTeX语法是否正确,是否有不必要的公式,确保行内公式正确使用$符号,独立公式单独成段。例如时间复杂度可以写成$O(1)$,如果有公式的话,比如循环队列的指针移动,可能需要写成数学表达式,但可能这里不需要复杂公式,所以可能没有。 总结步骤: 1. 定义队列,FIFO特性。 2. 特点:FIFO,存储方式,类型。 3. 工作原理:入队出队过程,数组和链表的实现差异。 4. 应用场景:任务调度、BFS、缓冲、阻塞队列等。 5. 优势:顺序性、解耦、缓冲。 6. 劣势:性能开销,功能限制。 7. 引用相关部分,比如链表实现的选择[^2],阻塞队列[^3]。 8. 生成相关问题。 现在开始组织语言,确保每个部分自然衔接,引用正确。</think>### 队列数据结构详解 #### 1. 队列的核心特点 - **先进先出(FIFO)**:元素按进入顺序处理,第一个入队的元素最先出队。 - **存储方式**: - **动态数组**:需处理扩容(如引用[2]中提到的动态数组实现栈时的扩容逻辑类似)[^2]。 - **链表**:更灵活,无需预分配空间(如引用[2]中解释链表实现队列的优势)。 - **类型扩展**: - **循环队列**:通过模运算实现数组空间的循环利用。 - **阻塞队列(BlockingQueue)**:支持线程安全操作,用于多线程协调。 - **优先队列**:按优先级出队,时间复杂度通常为$O(\log n)$。 #### 2. 工作原理 - **基本操作**: - **入队(Enqueue)**:元素添加到队尾,时间复杂度$O(1)$。 - **出队(Dequeue)**:移除队首元素,时间复杂度$O(1)$。 - **实现差异**: - **链表实现**: ```text 头节点(front) ← ... ← 尾节点(rear) 出队时删除头节点,入队时在尾部添加新节点。 ``` - **数组实现**: - 维护`front`和`rear`指针,初始均为$-1$。 - 循环队列通过`(rear + 1) % capacity`计算位置,避免空间浪费。 #### 3. 开发中的应用场景 - **任务调度**:如打印队列、消息队列(如RabbitMQ)。 - **算法应用**:广度优先搜索(BFS)依赖队列遍历层级节点。 - **缓冲机制**:网络请求的流量控制或数据流缓冲。 - **多线程协调**:阻塞队列用于生产者-消费者模型(如Java的`BlockingQueue`)。 #### 4. 优势与劣势 | **优势** | **劣势** | |-----------------------------------|-----------------------------------| | **顺序性保障**:公平处理任务 | **链表内存开销**:节点需存储指针 | | **解耦生产与消费**:异步处理 | **数组扩容成本**:动态调整需复制 | | **高并发缓冲**:削峰填谷 | **功能局限性**:需扩展变种如优先队列 | --- §§ 1. 循环队列如何解决数组实现的“假溢出”问题? 2. 阻塞队列在多线程编程中的具体实现机制是什么? 3. 优先队列的时间复杂度与堆结构有何关联? 4. 队列在BFS算法中的作用及其时间复杂度分析?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值