Java线程池实现多消费者批量处理队列消息

java性能优化之批量处理数据! 2、基于guava eventbus实现异步事件(实现简单,性能比较好,重启服务有丢失的风险)场景三、大批量消费消息队列消息的时候,如何提升数据处理效率,提高消费效率减少消息挤压?场景二:高并发下写数据,如何快速响应,如何提升数据处理效率,尽可能减轻数据库的压力?3、基于线程池实现异步处理(实现简单,并发量不大,可以用,重启服务有丢失的风险)1、发送数据到消息队列(比较推荐,基本不丢失,实现稍微麻烦)三、异步处理(这里简单聊下,后面有时间再详细讲解)三、批量处理数据的几种方案对比。controller层。 阅读详情

通常生产者-消费者的经典实现方式是,启动一个消费者线程从阻塞队列里获取消息进行消费,(多个)生产者往队列里插入待消费的数据然后立即返回。如果生产者生产的速率远大于消费者消费的速率,那么队列的待处理数据就会累积得越来越多。

本文的重点是封装实现(单个)消费者线程可以“批量处理”队列里的消息,这类似于多条数据插入数据库,由多次insert优化为一次batch insert。

顾名思义,“多消费者”就是开启多个消费者线程(其中每个消费者线程均可以批量处理队列消息),这里借用Java线程池来管理线程的生命周期:

首先,定义一个接口表示异步消费:

import java.util.concurrent.RejectedExecutionException;

/**
 * An object that accepts elements for future consuming.
 *
 * @param <E> The type of element to be consumed
 */
public interface AsynchronousConsumer<E> {

	/**
     * Accept an element for future consuming.
     *
     * @param e the element to be consumed
     * @return true if accepted, false otherwise
     * @throws NullPointerException if the specified element is null
     * @throws IllegalArgumentException if some property of this element
     *         prevents it from being accepted for future consuming
     * @throws RejectedExecutionException if the element
     *         cannot be accepted for consuming
     */
	public boolean accept(E e);
}

再定义一个消费者线程(由于线程托管给线程池管理了,这里是定义一个Runnable),多个Runnable通过共用BlockingQueue来实现多个消费者消费。这里 通过指定“batchSize”来表示批量处理(如果值大于1的话),TimeUnit指明了线程(Runnable)的最大空闲时间,超过该时间将会自动退出(如果值为0则表示永远等待)并提供beforeConsume、afterConsume、terminated等方法以便扩展:

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
 * An abstract {@code Runnable} of consumer for batches consuming Asynchronously
 *
 * @param <E> The type of element to be consumed
 */
public abstract class ConsumerRunnable<E> implements Runnable, AsynchronousConsumer<E>{
	
	/**
	 * for recording any exception
	 */
	private static final Log log = LogFactory.getLog(ConsumerRunnable.class);
	
	
	/**the elements queue**/
	private final BlockingQueue<E> queue;
	
	/**
	 * the batchSize for {@link #consume} used in every loop
	 */
	private volatile int batchSize;
	
	/**
     * Timeout in nanoseconds for polling an element from the
     * working queue(BlockingQueue).Thread uses this timeout to
     * wait up if the working queue is empty. 
     * zero means wait forever until an element become available.
     */
	private volatile long waitTime;
	
	/**
	 * If true then will cause run() quit
     * when waiting for element if interrupted
	 */
	private volatile boolean quitIfInterrupted = true;
	
	/**
	 * Allocates a new {@code ConsumerRunnable} object
	 * with the given initial parameters and default waitTime(0)
	 * which will cause the thread to wait forever until an element
	 * become available when the element queue is empty.
     * 
	 * @param queue the BlockingQueue for working
	 * @param batchSize the batch size used in {@link #consume} for every loop
	 * 
	 * @throws NullPointerException if the queue is null
	 * @throws IllegalArgumentException if the batchSize is less then or equal to zero
	 */
	public ConsumerRunnable(int batchSize, BlockingQueue<E> queue){
		this.batchSize = PracticalUtils.requirePositive(batchSize);
		this.queue = Objects.requireNonNull(queue);
	}
	
	/**
	 * Allocates a new {@code ConsumerRunnable} object
	 * with the given initial parameters.
	 * A time value of zero will cause the thread to wait forever
     * until an element become available when the element queue is empty.
     * 
	 * @param queue the BlockingQueue for working
	 * @param batchSize the batch size used in {@link #consume} for every loop
	 * @param time the time to wait. A time value of zero will cause
	 * the thread to wait forever until an element become available
	 * when the element queue is empty.
	 * @param unit the time unit of the time argument
	 * 
	 * @throws NullPointerException if any specified parameter is null
	 * @throws IllegalArgumentException if the batchSize is less then or equal to zero,
	 * or the time is less than zero
	 */
	public ConsumerRunnable(int batchSize, long time, TimeUnit unit,
			BlockingQueue<E> queue){
		this.batchSize = PracticalUtils.requirePositive(batchSize);
		this.queue = Objects.requireNonNull(queue);
		setWaitTime(time, unit);
	}
	
	
	/**
     * Accept an element for future consuming.
     * 
     * <p>Default implementation is equally invoke
     *  {@link java.util.Queue#offer queue.offer}
     *
     * @param e the element to be consumed
     * @throws NullPointerException if the specified element is null
     * @throws IllegalArgumentException if some property of this element
     *         prevents it from being accepted for future consuming
     * @throws RejectedExecutionException if the element
     *         cannot be accepted for future consuming (optional)
     * @return true if accepted, false otherwise
     */
	public boolean accept(E e){
		Objects.requireNonNull(e);
		return queue.offer(e);
	}
	
	
	/**
     * Main worker run loop.  Repeatedly gets elements from working queue
     * and consumes them, while coping with a number of issues:
     *
     * <p>1. Each loop run is preceded by a call to beforeConsume, which
     * might throw an exception, in which case the {@link #consume}
     * of the current loop will not be invoked.
     *
     * <p>2. Assuming beforeConsume completes normally, we consume the elements,
     * gathering any of its thrown exceptions to send to afterConsume. Any thrown
     * exception in the afterConsume conservatively causes the runnable to quit.
     *
     * <p>3. After {@link #consume} of the current loop completes, we call afterExecute,
     * which may also throw an exception, which will cause the runnable to quit.
     *
     */
	@Override
	public final void run() {
		starting();
		final List<E> list = new ArrayList<>();
		try{
			while(true){
				try {
					E info;
					int bSize = batchSize;
					for(int i=0;i<bSize&&list.size()<bSize&&(info=queue.poll())!=null;i++){
						list.add(info);
					}
					if(list.isEmpty()){
						try{
							beforeWait();
						} catch(Throwable ingnore){}
						//waiting if necessary until an element becomes available
						if(waitTime==0){
							list.add(queue.take());
						}else{
							E e = queue.poll(waitTime, TimeUnit.NANOSECONDS);
							if(e==null){//timeout
								break;
							}
							list.add(e);
						}
						
					}else{
						try{
							beforeConsume(list);
						}catch (Throwable t1) {
							list.clear();//clear to be reused for next loop
							log.error("The runnable["+getClass().getName()+"] which invoked beforeConsume occurred the exception:"
									+t1.toString(), t1);
							continue;
						}
						doConsume(list);
					}
				} catch (InterruptedException e) {
					if(quitIfInterrupted){
						Thread t = Thread.currentThread();
						if(!t.isInterrupted()){
							//restores the interrupted status
							t.interrupt();
						}
						break;
					}
				} catch(Throwable t){
					if(t instanceof Error){
						log.error("The runnable["+getClass().getName()+"] is about to die for the reason:"+t.toString(), t);
						break;
					}
					// else continue;
				}
			}
		} finally{
			try{
				//rechcek the list
				if(!list.isEmpty()){
					beforeConsume(list);
					doConsume(list);
				}
			} finally{
				terminated();
			}
		}
		
	}
	
	/**
	 * Consuming elements and ensure that the method
	 * {@code afterConsume} must be invoked in the end.
	 * @param list the elements that will be consumed
	 */
	private void doConsume(List<E> list){
		Throwable t = null;
		try {
			consume(list);
		} catch (Throwable t1) {
			t = t1;
			throw t1;
		} finally{
			try {
				afterConsume(list,t);
			} finally{
				list.clear();//clear to be reused for next loop
			}
		}
	}
	
	/**
	 * Returns the batchSize for {@link #consume} used in every loop
	 * 
	 * @return the batchSize
	 * 
	 * @see #setBatchSize
	 */
	public int getBatchSize(){
		return batchSize;
	}
	
	/**
     * Sets the batchSize for next loop used in {@link #consume}.
     * This overrides any value set in the constructor.
     *
     * @param batchSize the new batchSize
     * @throws IllegalArgumentException if the new batchSize is
     *         less than or equal to zero
     * @see #getBatchSize
     */
	public void setBatchSize(int batchSize){
		this.batchSize = PracticalUtils.requirePositive(batchSize);
	}
	
    
    /**
     * Returns the element queue used by this thread. Access to the
     * element queue is intended primarily for debugging and monitoring.
     * This queue may be in active use.  Retrieving the element queue
     * does not prevent queued element from being saved.
     *
     * @return the element queue
     */
    public BlockingQueue<E> getQueue() {
        return queue;
    }
    
    /**
     * Sets the time limit for the thread when waiting for
     * an element to become available. This overrides any value
     * set in the constructor. zero means wait forever.
     * Timeout will cause the runnable to quit.
     *
     * @param time the time to wait.  A time value of zero will cause
     *        the thread to wait forever until an element become available
     *        when the element queue is empty.
     * @param unit the time unit of the {@code time} argument
     * @throws IllegalArgumentException if {@code time} less than zero
     * @see #getWaitTime(TimeUnit)
     */
    public void setWaitTime(long time, TimeUnit unit) {
        long t = unit.toNanos(PracticalUtils.requireNonNegative(time));
        this.waitTime = t;
    }
    

    /**
     * Returns the thread waiting time, which is the time to wait up
     * when the working queue is  empty. zero means wait forever.
     *
     * @param unit the desired time unit of the result
     * @return the time limit
     * @see #setWaitTime(long, TimeUnit)
     */
    public long getWaitTime(TimeUnit unit) {
        return unit.convert(waitTime, TimeUnit.NANOSECONDS);
    }
    
    /**
     * Returns true if this runnable will quit
     * if interrupted when waiting for element.
     * @return true if this runnable will quit
     * if interrupted when waiting for element
     */
    public boolean isQuitIfInterrupted(){return quitIfInterrupted;}
    
    /**
     * Set quit or not if interrupted when waiting for element.
     * @param quitIfInterrupted true will cause run() quit
     * if interrupted when waiting for element
     */
    public void setQuitIfInterrupted(boolean quitIfInterrupted){
    	this.quitIfInterrupted = quitIfInterrupted;
    }
    
    
    /* Extension hooks */
    
    /**
     * Method invoked at the beginning of the run() method.
     * <p><b>Note:</b>if this method throw any exception then
     * any other method will not be invoked and cause the runnable to quit.
     */
    protected void starting() { }
    
    /**
     * Method invoked prior to current thread waiting for
     * an element to become available.
     * Any exception thrown by this method will be ignored.
     * 
     * <p>This implementation does nothing, but may be customized in
     * subclasses. Note: To properly nest multiple overridings, subclasses
     * should generally invoke {@code super.beforeWaiting} at the end of
     * this method.
     *
     */
    protected void beforeWait() { }

    /**
     * Method invoked prior to invoking {@link #consume} in the
     * current runnable for every loop.
     * 
     * <p><b>Note:</b>if this method throw any exception then the
     * {@link #consume} of current loop will not be invoked
     * 
     * <p>This implementation does nothing, but may be customized in
     * subclasses. Note: To properly nest multiple overridings, subclasses
     * should generally invoke {@code super.beforeConsume} at the end of
     * this method.
     *
     * @param list the elements that will be consumed
     */
    protected void beforeConsume(List<E> list) { }
    
    /**
	 * For subclass to implement the consume actions.
	 * 
	 * @param list the elements that will be consumed
	 */
	protected abstract void consume(List<E> list);
	
    /**
     * Method invoked upon one loop completion of {@link #consume} invoked.
     * This method is invoked by the current thread. If
     * non-null, the Throwable is the uncaught {@code Error}
     * that caused execution to terminate abruptly.
     *
     * <p>This implementation does nothing, but may be customized in
     * subclasses. Note: To properly nest multiple overridings, subclasses
     * should generally invoke {@code super.afterConsume} at the
     * beginning of this method.
     *
     * @param list the elements that has consumed(or to be consumed if the exception caused termination)
     * @param t the exception that caused terminati
Redis List队列与Java ThreadPoolExecutor集成实战 当线程池无法接受更多任务时(线程已达上限且队列已满),必须采取某种方式处理新增任务,这就是“拒绝策略”(Rejected Execution Handler)的作用。JDK 提供了四种内置策略,每种适用于不同的业务容忍度和系统架构需求。场景是否推荐使用 Runnable日志记录、事件通知✅ 强烈推荐定时清理临时文件✅ 推荐需要获取计算结果❌ 不适用可能抛出受检异常且需处理⚠️ 需手动包装 try-catch。 阅读详情

相关推荐

深入理解MPMC模式:多生产者和多消费者并发设计

在现代软件开发中,尤其是在需要处理高并发场景的应用程序中,理解并有效使用并发模型变得至关重要。MPMC(Multi Producer Multi Consumer)模式是并发编程中的一种常见模式,它允许多个生产者和多个消费者同时进行数据的生产和消费,从而极大地提高了程序处理任务的效率和吞吐量。MPMC模式的全称是“Multi Producer Multi Consumer”,它是一种特殊类型的生产者-消费者模型。

weixin_28793831的博客 1025

STM32G0——USB虚拟串口

STM32G0用cubeMX配置实现USB虚拟串口

阿昌 2321

从零编写linux0.11 - 第二章 内核初始化

编程环境:Ubuntu Kylin 16.04 代码仓库:https://gitee.com/AprilSloan/linux0.11-project linux0.11源码下载(不能直接编译,需进行修改) 1.添加内核 进入内核后也不是马上进入main函数,上一章只是暂时设置gdt,此外还要设置idt和其他的一些初始化操作,这些内容也要使用汇编进行编写,此处采用AT&T格式的汇编。 以下为head.s的内容,为了简单起见,内容还是一个死循环。 .globl startup_32 startup_3

皓空Fly的博客 1266

Dify与钉钉轻量级集成:打造企业内部AI助手

企业级聊天机器人开发已成为提升内部协同效率的关键技术。其核心原理在于通过API集成,将AI模型能力与即时通讯平台无缝连接,实现智能问答与自动化流程。从技术价值看,这种集成方案能够显著降低AI应用落地门槛,将前沿的AI能力快速转化为实际生产力。在应用场景上,它尤其适用于HR答疑、IT技术支持、产品知识库等高频内部服务场景。本文聚焦于Dify与钉钉的轻量级集成方案,通过Stream模式实现高效消息处理,并利用AI卡片支持流式输出,为企业构建体验自然的智能机器人提供了完整实践路径。该方案特别强调配置简化与部署便捷

weixin_27155667的博客 698

【谷粒商城】分布式事务与下单

=购物车== 构建gulimall-cart,复制静态资源到nginx,修改网关 购物车分为离线购物车和登录购物车 离线购物车重启浏览器了也还有 1、购物车需求 特点:读多写少,放入数据库并不合适 登录状态:登录购物车 放入数据库 mongodb 放入redis(采用) 登录以后,将离线购物车里合并后清空离线购物车 未登录状态:离线购物车 放入localstorage浏览器的技术 cookie WebSQL 放入redis(采用) 浏览器重启后还在 2、购物车VO (1) 数据结构分析

努力充实,远方可期 1万+

别再死记硬背Modbus报文了!用Python+Modbus Poll手把手教你调试工业设备

本文详细介绍了如何利用Python和Modbus Poll工具高效调试工业设备通信。通过实战案例和代码示例,帮助读者快速掌握Modbus通信协议的核心配置、数据监控技巧及Python自动化脚本编写,解决工业设备调试中的常见问题,提升工作效率。

weixin_42520780的博客 344

零基础入门simulink建模仿真--基于Simulink的QPSK调制与解调仿真建模示例

QPSK是一种相位调制技术,将两个比特映射到一个符号上,每个符号对应四个可能的相位之一(0°、90°、180°、270°)。通过Simulink,我们可以通过模块化的建模方式实现QPSK的调制和解调过程,并观察信号在信道中的传输效果。通过本指南,我们介绍了如何基于Simulink搭建一个完整的QPSK调制与解调仿真模型,并进行仿真和性能评估。背景介绍:理解QPSK调制与解调的基本原理及其应用。所需工具和环境:列出进行QPSK仿真所需的工具和环境。步骤详解。

amy_mhd的博客 1310

YOLO创新点改进点的消融实验设计规范

YOLO改进的消融实验是一种重要的方法,用于评估和验证模型中不同组件的影响。在进行YOLO改进的消融实验时,我们可以选择一些可能的改进点,比如Backbone网络、Anchor框设计和损失函数设计,然后通过实验来分析它们对模型性能的影响。首先,我们需要确定实验设计。对于每个改进点,我们将设计多个实验组,并针对每个实验组训练模型并进行性能评估。这里以三个改进点为例进行说明:改进点1:Backbone网络 (改进点2:Anchor框设计 (改进点3:损失函数设计 (

crasher123的博客 2912

如何对TBSS的结果进行统计学的分析,已完成置换检验,如何在最终的TBSS输出结果提取除具体值及如何做进一步相关性分析??

🏆本文收录于 《全栈 Bug 调优(实战版)》 专栏。专栏聚焦真实项目中的各类疑难 Bug,从成因剖析 → 排查路径 → 解决方案 → 预防优化全链路拆解,形成一套可复用、可沉淀的实战知识体系。无论你是初入职场的开发者,还是负责复杂项目的资深工程师,都可以在这里构建一套属于自己的「问题诊断与性能调优」方法论,助你稳步进阶、放大技术价值。

**My Coding Family** 1441

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

visio安装包

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

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

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

上一篇: Java采用Redis相关命令实现分布式锁
下一篇: O(n)算法快速构建树形结构(权限树、组织树、省市区联动树)
lf124
博客等级 码龄14年 8粉丝 7原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值