RecordAccumulator (一) getOrCreateDeque

本文深入探讨了Kafka中自定义的CopyOnWriteMap数据结构,解析其读写分离的设计理念,以及如何通过该结构提高封装批次流程的性能。特别介绍了在高并发场景下,CopyOnWriteMap如何实现线程安全并保持高效。

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

    private Deque<RecordBatch> getOrCreateDeque(TopicPartition tp) {

        /**
         * CopyonWriteMap:
         *      get
         *      put
         *
         */
        //直接从batches里面获取当前分区对应的存储队列
        Deque<RecordBatch> d = this.batches.get(tp);

        //我们现在用到是场景驱动的方式,代码第一次执行到这儿的死活
        //是获取不到队列的,也就是说d 这个变量的值为null
        if (d != null)
            return d;
        //代码继续执行,创建出来一个新的空队列,
        d = new ArrayDeque<>();
        //把这个空的队列存入batches 这个数据结构里面
        Deque<RecordBatch> previous = this.batches.putIfAbsent(tp, d);
        if (previous == null)
            return d;
        else
            //直接返回新的结果
            return previous;
    }

CopyOnWriteMap

/**CopyOnWriteMap的这样的一个数据类型。
        //这个数据结构在jdk里面是没有的,是kafka自己设计的。
        //这个数据结构设计得很好,因为有了这个数据结构
        //整体的提升了封装批次的这个流程的性能!!
        //JDK  juc包下面:CopyOnWriteArrayList*/
        this.batches = new CopyOnWriteMap<>();

/**
 * A simple read-optimized map implementation that synchronizes only writes and does a full copy on each modification
 *
 * 1) 这个数据结构是在高并发的情况下是线程安全的。
 * 2)  采用的读写分离的思想设计的数据结构
 *      每次插入(写数据)数据的时候都开辟新的内存空间
 *      所以会有个小缺点,就是插入数据的时候,会比较耗费内存空间。
 * 3)这样的一个数据结构,适合写少读多的场景。
 *      读数据的时候性能很高。
 *
 * batchs这个对象存储数据的时候,就是使用的这个数据结构。
 * 对于batches来说,它面对的场景就是读多写少的场景。
 *
 *batches:
 *   读数据:
 *      每生产一条消息,都会从batches里面读取数据。
 *      假如每秒中生产10万条消息,是不是就意味着每秒要读取10万次。
 *      所以绝对是一个高并发的场景。
 *   写数据:
 *     假设有100个分区,那么就是会插入100次数据。
 *     并且队列只需要插入一次就可以了。
 *     所以这是一个低频的操作。
 *
 *     高性能:
 *
 *      读写分离读设计方案:适合的场景就是读多写少。
 *          读多:
 *          写少:
 */
public class CopyOnWriteMap<K, V> implements ConcurrentMap<K, V> {
    /**
     * 核心的变量就是一个map
     * 这个map有个特点,它的修饰符是volatile关键字。
     * 在多线程的情况下,如果这个map的值发生变化,其他线程也是可见的。
     *
     * get
     * put
     */
    private volatile Map<K, V> map;
     
        /**
     * 没有加锁,读取数据的时候性能很高(高并发的场景下,肯定性能很高)
     * 并且是线程安全的。
     * 因为人家采用的读写分离的思想。
     * @param k
     * @return
     */
    @Override
    public V get(Object k) {
        return map.get(k);
    }

        /**
     * 1):
     *      整个方法使用的是synchronized关键字去修饰的,说明这个方法是线程安全。
     *      即使加了锁,这段代码的性能依然很好,因为里面都是纯内存的操作。
     * 2)
     *        这种设计方式,采用的是读写分离的设计思想。
     *        读操作和写操作 是相互不影响的。
     *        所以我们读数据的操作就是线程安全的。
     *3)
     *      最后把值赋给了map,map是用volatile关键字修饰的。
     *      说明这个map是具有可见性的,这样的话,如果get数据的时候,这儿的值发生了变化,也是能感知到的。
     * @param k
     * @param v
     * @return
     */
    @Override
    public synchronized V put(K k, V v) {
        //新的内存空间
        //读写分离
        //往新的内存空间里面插入
        //读,读数据就老读空间里面去
        Map<K, V> copy = new HashMap<K, V>(this.map);
        //插入数据
        V prev = copy.put(k, v);
        //赋值给map
        this.map = Collections.unmodifiableMap(copy);
        return prev;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值