消费者一次性取出全部数据的阻塞队列(java自定义)

文章描述了一个Java类TakeAllQueue,用于解决生产者性能高于消费者的问题,通过使用ReentrantLock和Condition实现读写等待,批量插入ES,显著提升了性能。作者通过示例展示了如何在多线程场景中使用这个队列进行同步处理。

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

想起很久以前写的一个工具,记录下吧。

原因: 当时要做一个优化, 大量的数据拉取后,需要同步到 ES,之前是多个线程一条条的插入,

这里就典型的生产消费问题, 生产一条,丢到线程池消费一条,然而当时的生产者性能远的高于消费者性能,于是决定做这样一个队列,生产者一条一条的产生数据后进入队列,消费者等待队列装满或者1秒以后还没装满就去消费。

这样改造可以一次批量插入几十条数据到 ES,实际性能提升了很多(具体提升多少忘了)

大概实现:“读” “写”相互等待,“写”满时或者达到超时时间唤醒 ”读“,“读”完立即唤醒 “写”

public class TestSome {

    @Slf4j
    public static class TakeAllQueue<T> {
        private final ReentrantLock lock = new ReentrantLock();
        private int max;
        private List<T> list;
        private Condition addCondition = lock.newCondition();
        private Condition readCondition = lock.newCondition();
        // 加了锁,所以用普通的int也能计数,这里用的AtomicInteger,
        //当前缓存中的数量
        private volatile AtomicInteger counting = new AtomicInteger(0);
        //已添加数量
        private volatile AtomicInteger totalCounting = new AtomicInteger(0);
        //已读取数量
        private volatile AtomicInteger finishedCounting = new AtomicInteger(0);

        /**
         * @param max
         * @return
         * @Author yq
         * @Description 指定最大max个线程完成任务后就开始读取数据
         **/
        public TakeAllQueue(int max) {
            this.max = max;
            this.list = new ArrayList<>(max);
        }

        /**
         * 添加数据,阻塞方式
         * @param obj
         * @return void
         * @Author yq
         * @Date 17:41 2020-12-9
         **/
        public void add(T obj) {
            try {
                lock.lockInterruptibly();
                while (counting.get() >= max) {
                    addCondition.await();
                }
                counting.addAndGet(1);
                totalCounting.addAndGet(1);
                list.add(obj);
                readCondition.signal();
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            } finally {
                lock.unlock();
            }
        }

        private List<T> readAll() {
            List<T> result = new ArrayList<>(list);
            list.clear();
            counting.set(0);
            addCondition.signal();
            finishedCounting.addAndGet(result.size());
            return result;
        }

        /**
         * 超过一定时间直接获取,超时时间内,list装满,直接返回, 单位:秒!!!
         * @param secondsTimeout   秒数
         * @return java.util.List<T>
         * @Author yq
         * @Description
         **/
        public List<T> getAllBlocking(long secondsTimeout) {
            try {
                lock.lock();
                long expire = System.currentTimeMillis() + secondsTimeout * 1000;
                while (counting.get() < max) {
                    if (expire <= System.currentTimeMillis()) {
                        break;
                    }
                    addCondition.signal();
                    readCondition.await(secondsTimeout, TimeUnit.SECONDS);
                }
                List<T> result = readAll();
                addCondition.signal();
                return result;
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return Collections.emptyList();
            } finally {
                lock.unlock();
            }
        }

        public boolean isFinished() {
            return finishedCounting.get() >= totalCounting.get();
        }

        public int getCount() {
            return totalCounting.get();
        }
    }

    public static void main(String[] args) {
        TakeAllQueue<Integer> queue = new TakeAllQueue<>(20);
        //生产者
        ExecutorService producer = Executors.newFixedThreadPool(2);
        //消费者
        ExecutorService consumer = Executors.newFixedThreadPool(10);

        //生产奇数
        producer.execute(() -> {
            for (int i = 1; i < 10000; i += 2) {
                queue.add(i);
            }
        });

        //生产偶数
        producer.execute(() -> {
            for (int i = 0; i < 10000; i += 2) {
                queue.add(i);
            }
        });


        //20个消费者同事消费
        for (int i = 0; i < 20; i++) {
            consumer.execute(() -> {
                while (queue.getCount() < 10000) {
                    try {
                        //模拟慢消费,
                        ThreadLocalRandom random = ThreadLocalRandom.current();
                        long sleepTime = random.nextLong(100);
                        Thread.sleep(sleepTime);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    List<Integer> result = queue.getAllBlocking(1);
                    System.out.println(Thread.currentThread().getName() + " 消费数量 " + queue.getCount() + "  "   + result + "  总数量 " + result.size());
                }
            });
        }


    }
}

运行结果如下:确实是每次拉取20条数据

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值