Queue & Map 武魂融合技:MapQueueQueue & Map 武魂融合技:MapQueue

QT blockingFilter blockingMap blockingMapped 当mapFunction被并发调用时,一次只有一个线程会调用reduceFunction。reduceFunction的调用顺序由reduceOptions决定。按顺序为每个项目调用mapFunction一次。每个mapFunction的返回值被传递给reduceFunction。主要作用是筛选出符合条件的项值结果集,并与之替换原有序列列表。不直接修改容器的每一项,而是将处理后的结果返回一个新的容器。可以直接修改容器的每一项。 阅读详情

读前问题:Map的特性和Queue的特性相结合,大家会联想到什么功能呢?

接下来看看我的实现是不是和你的想法不谋而合

背景

前一段时间维护的老项目一直出现MQ消息堆积的问题

其实这个消息量本身我觉得不算是很大,但可能是之前的代码问题,导致消息一直堆积

这个老项目是用于管理设备状态的,一些设备上报的数据会通过中台服务推到MQ中,然后这个项目会监听这部分消息更新设备状态

之前这个项目一直跑的好好的,一共差不多就几百个设备,虽然存在少量的消息堆积,但是设备在每天的一个时间段是不会上报数据的,这个项目在设备不上报数据的这段时间里能把之前堆积的少量消息给处理完,所以等于是达到了一个平衡

后面由于项目需求又加了一批设备,然后MQ消息堆积的问题就被放大了

又因为之前的同事离职了,然后。。。dddd

最初的设计

在我研究了一段时间后(问问同事,看看代码)我发现他们之前用了最土豪的方式

那就是每收到一条数据就在数据库更新一次

这还不是最骚的

当时他们将设备的属性设计成了动态的,也就是说会有一张表来单独保存属性

id

device

attr

value

1

10001

brightness

100

2

10001

power

30

3

10002

brightness

80

4

10002

power

20

...

...

...

...

类似上面这样的方式来保存设备的属性

所以设备的一个属性就等于一条记录,而有些设备一次性会保存几十条属性

而且他们还在更新属性的方法上还加了@Transactional

可能当时认为这个项目的体量不会很大,就怎么简单怎么来?

我仿佛能看到这个老项目一边没日没夜的处理数据一边撕扯着嗓子:臣妾做不到啊!

问题分析 & 解决方案

其实这个问题就是服务处理数据的速度跟不上设备上报数据的速度

研究了解的需求和代码之后有了一些优化的思路

当时就罗列了下所有能想到的解决方案再进行对比评估

扩展服务/MQ/硬件资源(老项目能不动就不动)

根据我们之前的了解,其实代码端就有很大的优化空间,况且如果瓶颈是在数据库的话,单纯扩展服务或MQ估计没有什么效果,而且还需要额外的维护和费用

加大设备上报的时间间隔(老项目能不动就不动)

同样的,一个是代码端有很大的优化空间,而且这个方案治标不治本,如果后续设备数量又增加,不能保证不会再次出现这个问题

最重要的是,就几百个设备,我可没有这个脸提这个方案

优化底层更新逻辑(这下不得不修改代码)

如果要在代码层面进行优化,首先这些设备属性可以直接在Redis中更新,然后定时刷到数据库

同时如果属性的值没有变化的话甚至可以直接忽略,这样在更新数据库的时候进一步减少了数据量

但是,这种方式需要花费大量额外的时间来排查可能引发的连锁问题,特别是有很多基于设备属性更新而联动的其他模块的业务功能,而我并不想了解其他的业务模块(白眼)

使用背压策略(这下不得不修改代码)

如果大家接触过RxJava应该会了解对于生产者的生产速度大于消费者的消费速度时,我们可以用特定的背压策略来避免一些问题

由于这个项目接收的数据只是用于更新数据库,没有其他的处理,所以我们只需要关注最新的数据就行了,在这之前的数据直接丢弃也没有太大问题

这样的话只需要在接受数据和处理数据之间加一个背压策略

//只是思路,非真实代码publicclassReceiver {//背压策略private Backpressure backpressure = 
        new Backpressure().onEach(this::handle);

    //接收数据-背压publicvoidreceiveNew(Object data){
        backpressure.put(data);
    }

    //接收数据-原有方式publicvoidreceive(Object data){
        handle(data);
    }

    //处理数据publicvoidhandle(Object data){
        //省略
    }
}

就像上面的代码,收到数据后放入Backpressure中,Backpressure会将大部分中间状态的数据丢弃,将最新的状态回调到onEach中

如果真的能实现这样的背压策略,就可以少处理很多数据,同时改动很小

(这里其实也可以直接把接收到的数据放到Redis,然后用定时任务拉Redis中的数据更新数据库,相当于是把上一个方案的逻辑移到最外层,但是我在最开始竟然没想到!)

只处理每个设备最新数据的背压策略

为了实现上述背压的功能,我们需要有一个数据筛选模型,也就是我们怎么判断哪些数据要处理哪些数据要丢弃

设计一个缓存队列,当接收到新的数据而队列已经满了的时候,就丢掉最早的数据?

这样确实能达到类似的效果,但是有可能只能处理到一部分设备数据,而另一部分设备数据永远都被丢弃

那么怎么保证每个设备都能处理到呢?

如果能有这样一个队列,当接收到一个新的设备数据时,如果当前队列中已经存在对应设备的数据就更新这个设备数据,否则在最后添加这条设备数据

简单模拟一下流程,如下:

  1. 设备A上报数据A1,当前队列:A1

  1. 设备B上报数据B1,当前队列:A1,B1

  1. 设备A上报数据A2,当前队列:A2,B1

  1. 进行一次消费,最前面的A2被消费,当前队列:B1

  1. 设备B上报数据B2,当前队列:B2

  1. 设备A上报数据A3,当前队列:B2,A3

  1. 设备B上报数据B3,当前队列:B3,A3

  1. 进行一次消费,最前面的B3被消费,当前队列:A3

在队列的基础上,存在的就更新,没有的就添加,这不就是Map的特性么?而且LinkedHashMap就支持放入的顺序

如果能让Queue和Map结合在一起,说不定就能实现这样的功能!

而且我们可以利用BlockingQueue的特性,使用对应的阻塞方法,用put入队,用take出队

改造 LinkedBlockingQueue(JDK8)

我们可以组合LinkedBlockingQueue和LinkedHashMap来实现我们需要的功能

改造LinkedBlockingQueue将内部的链表改为LinkedHashMap,这样相同的设备数据会覆盖,同时会按照上报的顺序进行消费,保证每个设备某一时刻的最新数据都会被消费

为什么是改造LinkedBlockingQueue而不是ArrayBlockingQueue,因为LinkedBlockingQueue用的是两把锁,入队和出队各一把锁,性能应该更好一点

将Node链表改为Map

我们先添加我们的Map属性用于替换链表的操作

先来看看LinkedBlockingQueue原本的链表属性

publicclassLinkedBlockingQueue<E> extendsAbstractQueue<E>
        implementsBlockingQueue<E>, java.io.Serializable {/**
     * Linked list node class
     */staticclassNode<E> {
        E item;

        /**
         * One of:
         * - the real successor Node
         * - this Node, meaning the successor is head.next
         * - null, meaning there is no successor (this is the last node)
         */
        Node<E> next;

        Node(E x) { item = x; }
    }

    /**
     * Head of linked list.
     * Invariant: head.item == null
     */
    transient Node<E> head;

    /**
     * Tail of linked list.
     * Invariant: last.next == null
     */private transient Node<E> last;

}

现在我们去掉链表的这些内容,同时添加我们的Map属性

改完之后我们的属性大概是这样的,其他的属性都保留

//暂时就叫 LinkedBlockingMapQueue 吧publicclassLinkedBlockingMapQueue<K, V> {

    /**
     * The capacity bound, or Integer.MAX_VALUE if none
     */privatefinal int capacity;

    /**
     * Current number of elements
     */privatefinal AtomicInteger count = new AtomicInteger();

    /**
     * Lock held by take, poll, etc
     */privatefinal ReentrantLock takeLock = new ReentrantLock();

    /**
     * Wait queue for waiting takes
     */privatefinal Condition notEmpty = takeLock.newCondition();

    /**
     * Lock held by put, offer, etc
     */privatefinal ReentrantLock putLock = new ReentrantLock();

    /**
     * Wait queue for waiting puts
     */privatefinal Condition notFull = putLock.newCondition();

    //我们添加的 Mapprivatefinal Map<K, V> map = new LinkedHashMap<>();

}

简单的介绍一下

capacity是队列的容量

count是当前元素的数量

takeLock是出队锁

notEmpty用来控制是否可以出队

putLock是入队锁

notFull用来控制是否可以入队

改造 put 方法

我们先来看下原本的put方法

/**
 * Inserts the specified element at the tail of this queue, waiting if
 * necessary for space to become available.
 *
 * @throws InterruptedException {@inheritDoc}
 * @throws NullPointerException {@inheritDoc}
 */publicvoidput(E e)throws InterruptedException {
    if (e == null) thrownew NullPointerException();
    // Note: convention in all put/take/etc is to preset local var// holding count negative to indicate failure unless set.int c = -1;
    Node<E> node = new Node<E>(e);
    final ReentrantLock putLock = this.putLock;
    final AtomicInteger count = this.count;
    putLock.lockInterruptibly();
    try {
        /*
         * Note that count is used in wait guard even though it is
         * not protected by lock. This works because count can
         * only decrease at this point (all other puts are shut
         * out by lock), and we (or some other waiting put) are
         * signalled if it ever changes from capacity. Similarly
         * for all other uses of count in other wait guards.
         */while (count.get() == capacity) {
            notFull.await();
        }
        enqueue(node);
        c = count.getAndIncrement();
        if (c + 1 < capacity)
            notFull.signal();
    } finally {
        putLock.unlock();
    }
    if (c == 0)
        signalNotEmpty();
}

/**
 * Links node at end of queue.
 *
 * @param node the node
 */privatevoidenqueue(Node<E> node){
    // assert putLock.isHeldByCurrentThread();// assert last.next == null;
    last = last.next = node;
}

加上入队锁之后,当前队列不满的状态下,将新的节点放到链表最后,同时count+1,如有必要再唤醒一下其他线程

我们只要把enqueue方法改成往Map中添加即可

public V put(K k, V v)throws InterruptedException {
    int c;
    V x;
    final ReentrantLock putLock = this.putLock;
    final AtomicInteger count = this.count;
    putLock.lockInterruptibly();
    try {
        //从这里开始修改if (map.containsKey(k)) {
            x = map.put(k, v);
            //如果已经存在则数量不增加
            c = count.get();
        } else {
            //如果已经存在,只需要修改不会增加数量//只有需要增加count才要等待while (count.get() == capacity) {
                notFull.await();
            }
            x = map.put(k, v);
            //如果不存在则数量+1
            c = count.getAndIncrement();
        }
        //到这里修改结束if (c + 1 < capacity) 
            notFull.signal();
    } finally {
        putLock.unlock();
    }
    if (c == 0) 
        signalNotEmpty();
    return x;
}

这里需要注意的一点是,如果Map中已经存在Key那么count就不用加了

改造 take 方法

take方法也如法炮制

我们先看下原本的take方法

public E take()throws InterruptedException {
    E x;
    int c = -1;
    final AtomicInteger count = this.count;
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lockInterruptibly();
    try {
        while (count.get() == 0) {
            notEmpty.await();
        }
        x = dequeue();
        c = count.getAndDecrement();
        if (c > 1)
            notEmpty.signal();
    } finally {
        takeLock.unlock();
    }
    if (c == capacity)
        signalNotFull();
    return x;
}

/**
 * Removes a node from head of queue.
 *
 * @return the node
 */private E dequeue(){
    // assert takeLock.isHeldByCurrentThread();// assert head.item == null;
    Node<E> h = head;
    Node<E> first = h.next;
    h.next = h; // help GC
    head = first;
    E x = first.item;
    first.item = null;
    return x;
}

加上出队锁之后,当前队列不空的状态下,将最前面的节点拿出来,同时count-1,如有必要再唤醒一下其他线程

我们只要把dequeue方法改成从Map中移除即可

public Map.Entry<K, V> take()throws InterruptedException {
    Map.Entry<K, V> x;
    int c;
    final AtomicInteger count = this.count;
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lockInterruptibly();
    try {
        while (count.get() == 0) {
            notEmpty.await();
        }

        //从这里开始修改
        Iterator<Map.Entry<K, V>> iterator = map.entrySet().iterator();
        x = iterator.next();
        iterator.remove();
        //到这里修改结束

        c = count.getAndDecrement();
        if (c > 1) 
            notEmpty.signal();
    } finally {
        takeLock.unlock();
    }
    if (c == capacity) 
        signalNotFull();
    return x;
}

嘿嘿,大功告成!

该吃饭吃饭,该下班下班,该睡觉睡觉,该干嘛干嘛

事情顺利的好像有点不太对劲

在回家了路上我一直在想LinkedBlockingQueue和ArrayBlockingQueue的锁的数量问题

ArrayBlockingQueue用一把锁是不是因为操作的是同一个数组,容易出现冲突或是处理冲突的判定非常复杂

而LinkedBlockingQueue用的是链表,每个节点相对独立,头部节点出队和在尾部添加节点不会相互影响

那么我现在把链表改为Map会有问题么?虽然我们已经控制了不能同时入队或同时出队,但是在入队的同时出队是没有限制的

入队问题

我们再来回顾一下我们的put方法的其中一段

//加锁if (map.containsKey(k)) {
    //如果已经存在对应的 key
    x = map.put(k, v);
    //如果已经存在则数量不增加
    c = count.get();
} else {
    //省略
}
//解锁

假设我们现在的队列里面有{"A": "A1"}这一个元素

现在我们调用方法put方法添加元素{"A": "A2"}

由于已经存在A对应的元素,所以会进入上面的这个if分支

这个时候,另一个线程调用take方法把{"A": "A1"}取出,count变为0

接着执行map.put(k, v)后,map中的元素数量为1而count还是0

这不就。。。出大问题了吗

我们直接调试一下是不是真的会出现这样的情况

我写了一段测试的代码,先添加一个{"A": "A1"},然后在添加{"A": "A2"}之前打上断点,运行

可以看到现在count = 1然后map里面有一个元素{"A": "A1"}

然后一步一步跟进去

果然进到了if分支里面,这个时候我们调用一下take方法

可以看到count = 0同时map为空了

然后接着执行map.put(k, v)方法

好家伙,map的size = 1而count = 0

由于我们之前判断了A存在,所以不会进行count + 1的操作

解决方案

一种方案是去掉count的维护逻辑,数量直接通过map.size()获取

但是这样就需要Map在一定程度上线程安全,而LinkedHashMap是线程不安全的

难道我们还要改造一下LinkedHashMap,让它在同时读写的时候能够不出问题?

还有另一种方案

那就是把锁改成一把,出队的时候就不能入队,入队的时候就不能出队

果断选择了这种方案

改成一把锁之后,整个逻辑也变得更简单了

public V put(K key, V value)throws InterruptedException {
    V x;
    lock.lockInterruptibly();
    try {
        if (map.containsKey(k)) {
            //已经存在//更新节点//count不变
            x = map.put(k, v);
        } else {
            //不存在while (count == capacity) {
                //满了,等未满的信号
                notFull.await();
            }
            //添加节点//count+1
            x = map.put(k, v);
            count++;
        }
        //发送未空信号
        notEmpty.signal();
    } finally {
        lock.unlock();
    }
    return x;
}

public Map.Entry<K, V> take()throws InterruptedException {
    lock.lockInterruptibly();
    try {
        while (count == 0) {
            notEmpty.await();
        }
        Iterator<Map.Entry<K, V>> iterator = map.entrySet().iterator();
        Map.Entry<K, V> entry = iterator.next();
        iterator.remove();
        count--;
        notFull.signal();
        return entry;
    } finally {
        lock.unlock();
    }
}

来看看效果,先写一个Demo

@Slf4j
publicclassMapQueueSample {private final Map<String, String> map;

    private final BlockingQueue<String> queue;

    publicMapQueueSample(){
        BlockingMapQueue<String, String> bmq = new LinkedBlockingMapQueue<>();
        bmq.addSynchronizer(new MapQueue.Synchronizer<String, String>() {

            @Override
            publicvoid afterEnqueue(String key, String value, Map<String, String> readOnly) {
                log.info("Put: " + value + ", Current: " + readOnly.toString());
            }

            @Override
            publicvoid afterDequeue(String key, String value, Map<String, String> readOnly) {
                log.info("Take: " + value + ", Current: " + readOnly.toString());
            }
        });

        this.map = bmq.map();
        this.queue = bmq.queue();
    }

    publicvoidstart(){
        startPut("A");
        startPut("B");
        startPut("C");
        startPut("D");
        startTake();
    }

    publicvoidstartPut(String s){
        Thread thread = new Thread() {

            int i;

            @SneakyThrows
            @Override
            publicvoidrun(){
                while (true) {
                    String v = s + i++;
                    map.put(s, v);
                    Thread.sleep(500);
                }
            }
        };
        thread.setName("Put" + s);
        thread.start();
    }

    @SneakyThrows
    publicvoidstartTake(){
        while (true) {
            String s = queue.take();
            Thread.sleep(1200);
        }
    }
}

开4个线程放ABCD,然后主线程不断读取

就是我们要的效果!

结束

我觉得我现在已经可以单独写一篇LinkedHashMap的源码分析了

然后我将LinkedBlockingMapQueue发布到了Maven中央仓库上

简单介绍一下集成和使用

implementation 'com.github.linyuzai:concept-mapqueue-core:1.1.0'

或

<dependency><groupId>com.github.linyuzai</groupId><artifactId>concept-mapqueue-core</artifactId><version>1.1.0</version></dependency>

使用方式如下

BlockingMapQueue<String, String> bmq = new LinkedBlockingMapQueue<>();

//通过 Map 操作Map<String, String> map = bmq.map();

//通过 Queue 操作
BlockingQueue<String> queue = bmq.queue();

通过map()和queue()可以获得对应的实例调用对应的方法

武魂奶妈源码.zip 易语言代码可供新手参考 学习之用 不是深奥只是基础类的东西 立即下载

相关推荐

Blocking Map for Java:基于地图的同步器-开源

阻塞图是另外支持在检索元素时等待键可用的操作的图。 阻塞映射在并发环境中充当生产者和消费者之间的同步器。 注意:该项目现在托管在GitHub(https://github.com/sarveswaran-m/blockingMap/wiki)

Java之Queue与Map集合

一、Queue 1.特点: 和数据结构中的队列类似,只允许在队头删除元素,队尾插入元素。队列中允许有重复的元素。 2.Queue中的方法 2.1 插入元素 boolean add(E element) boolean offer(E element) 说明:二者都是向队列中添加元素,如果成功,返回true。区别在于当队列已满时,add()抛出IllegalStateExcepti...

smell201611010513的博客 1544

用户存续期价值评估(CLV) 一

用户存续期价值评估(CLV) 1 一. 定义 ​ &nbsp;&nbsp;&nbsp;&nbsp;用户存续期价值(Customer Lifetime Value),也可称为CLTV、LCV或者LTV,是企业对未来一个客户与其的关系所带来的净收益的预测。这个预测模型可以有不同水平的复杂度和精确度,从一个直观感觉到使用复杂的预测技术。CLV可以被简单理解为客户关系的价值,也就是未来由于与客户的关系所带...

weixin_43171270的博客 8025

java--线程安全集合 CopyOnWriteArrayList、ConcurrentHashMap、ConcurrentSkipListMap、BlockingQueue

概要 线程安全集合可以分为三大类: 从以前 jdk 传承下来的 Hashtable、Vector; 使用Collections装饰的线程安全集合,如:Collections.synchronizedCollection、Collections.synchronizedList、Collections.synchronizedMap、Collections.synchronizedSet ...

grey_mouse的博客 833

高通平台Android稳定性调试笔记:手把手教你用T32、Crash Utility分析Kernel Panic与RAM Dump

本文详细介绍了高通平台Android内核崩溃的分析方法,包括使用T32 Simulator和Crash Utility工具链解析Kernel Panic与RAM Dump。从环境配置、dump文件预处理到高级调试技巧,手把手教你如何定位和解决Android系统崩溃问题,特别适用于MSM/SA8155平台的开发者。

weixin_42543046的博客 526

一文讲透什么是ADC/DAC中的DDC、DUC、采样率以及数据率等概念

摘要:ADC/DAC中的采样率与数据率概念不同,采样率是转换器的时钟速率,而数据率是数字输出的实际速率。抽取通过滤波降低数据速率,提升信噪比但牺牲带宽;内插则通过插零和滤波提高采样率,将镜像信号移远但增加延迟和功耗。JESD204B标准的高速转换器需平衡通道数与串行速率,内插/抽取技术是关键。文章还解析了DDC、DUC的应用及优缺点,为高速数据转换设计提供参考。(150字)

weixin_49761275的博客 4264

QtConcurrent命名空间

1、enum QtConcurrent::ReduceOption:此枚举指定将 map 或 filter 函数的结果传递给 reduce 函数的顺序。

hw5230的博客 594

labview汽车电子通用测试程序架构,CAN通讯上位机

结合做过的汽车电子测试项目,车身控制器测试,CAN网络模块测试,电机控制器功能测试,汽车仪表盘视觉及功能测试、电气盒功能测试、EOL下线检测、车用空调控制器功能测试等项目,结合实际项目,整理编写的一个汽车电子,通用测试架构,包含项目的讲解等。6、数据保存 、数据处理、 曲线显示 、报警处理等。7、CAN总线故障信息监控及记录保存。2、参数设置(CAN端口及参数)5、CAN通讯+数据解析。

ali2805486883的博客 2753

java map 队列_Java:queue队列,map集合

该楼层疑似违规已被系统折叠隐藏此楼查看此楼Queue: 基本上,一个队列就是一个先入先出(FIFO)的数据结构Queue接口与List、Set同一级别,都是继承了Collection接口。LinkedList实现了Deque接 口示例代码:1. import java.util.LinkedList;2. import java.util.Queue;3.4. publicclassQue...

weixin_28689729的博客 780

安装破解IAR EWARM 8.2,提供注册机

IAR Embedded Workbench for ARM(IAR for ARM)是一款微处理器的集成开发环境软件,该集成开发环境中包含了IAR的C/C++编译器、 汇编工具、链接器、库管理器、文本编辑器、工程管理器和C-SPY调试器,支持ARM,AVR,MSP430等芯片。   一、安装 双击运行安装程序,点击Install IAR Embedded Workbench for ARM...

睿思派克 4万+

Excel-MCP实战指南:零代码实现大模型与Excel的智能交互

本文详细介绍了Excel-MCP工具如何实现大模型与Excel的无缝交互,无需编写代码即可完成数据读写、图表生成等复杂操作。通过实战案例展示了Excel-MCP在销售报表自动化、财务对账等企业级场景中的应用,大幅提升工作效率。文章还提供了安装指南、核心功能解析和常见问题解决方案。

weixin_29212429的博客 433

Qt 多线程并发高阶类QtConcurrent 的使用

#include &lt;QCoreApplication&gt; #include &lt;QtConcurrent&gt; #include &lt;QDebug&gt; #include &lt;iostream&gt; #include &lt;csignal&gt; #include &lt;unistd.h&gt; using namespace std; int g_1 = 0; // 测试中断方式 void signalHandler(int signum) { qDebug(

weixin_44075027的博客 1465

Python实战:用NumPy实现LU分解与QR分解(附代码对比)

本文深入探讨了使用NumPy实现LU分解与QR分解的实战方法,通过代码对比分析了两种矩阵分解技术的原理、数值稳定性差异及核心应用场景。LU分解适用于高效求解线性方程组,而QR分解凭借其正交化特性,在求解最小二乘问题和处理病态矩阵时更具优势。文章提供了详细的Python代码示例,帮助开发者根据具体问题选择最合适的分解方法。

php55的博客 1212

Java:queue队列,map集合

Queue: 基本上,一个队列就是一个先入先出(FIFO)的数据结构 Queue接口与List、Set同一级别,都是继承了Collection接口。LinkedList实现了Deque接 口 示例代码: 1. import java.util.LinkedList; 2. import java.util.Queue; 3. 4. public class QueueDemo { 5. ...

weixin_42530700的博客 1351

3.DJI-payload SDK自定义控件的设计:

自定义控件(Widget)是指负载运行时,在用户界面中显示的自定义 UI 控件,以帮助用户快速查看负载独特的设备状态,或者对负载设进行设置。这些 UI 控件可以显示在以下用户界面中。Pilot 2 应用程序;大疆智图;基于 MSDK 开发的应用程序。这种自定义控件并不是通过 MSDK 实现的,而是通过 PSDK 的配置实现的。。

哒宰的博客 770

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

采用map,queue结合的方式执行广度优先算法,并导出最短路径

采用map,queue结合的方式执行广度优先算法,并导出最短路径 #include &lt;iostream&gt; #include &lt;vector&gt; #include &lt;queue&gt; #include &lt;map&gt; #include &lt;stack&gt; using namespace std; struct Node { bool State[4] = {false,false,false,false}; }; bool findMazePathB

qq_44760982的博客 270

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

scala学习总结7(Queue、Map)

队列:Queue 基本说明: ①队列是一个有序列表,底层用数组或是链表实现; ②遵循先入先出原则 ③scala中,有scala.mutable.collection.Queue(常用) 和scala.immutable.collection.Queue 代码示例1(添加元素): object TestQueue { def main(args: Array[String]): Uni...

zy 241

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

map/queue的遍历

1.遍历map/unordered_map for(unordered_map&amp;lt;int,int&amp;gt;::iterator iter = freq.begin(); iter != freq.end(); iter ++ ) 2.遍历queue/priority_queue while(!pq.empty()){ /*一顿操作*/ pq.pop(); }  ...

low米的博客 3493

Java学习 day32_Queue&Map

Queue 1.1 特点 1, Queue接口是Collection一个子接口, 描述的数据结构是队列 2, Queue存储元素是有序 3, Queue 允许存储重复元素 4, Queue 不允许存储null元素( LinkedList除外) Queue的删除操作, 是以返回一个null值作为 标记, 标记没有元素存储了 1.2 API 1.2.1 注意 1, 如果Queue存储元素的时候, 没有位置/空间 可以存储了, 如果使用add方法抛出异常, 如果使用offer返回布尔值 假 2, 如果Qu

weixin_43385930的博客 516

Qt之Concurrent Map和Map-Reduce

简述 QtConcurrent::map()、QtConcurrent::mapped() 和 QtConcurrent::mappedReduced() 函数对一个序列中(例如:QList、QVector)的项目并行地进行计算。QtConcurrent::map() 就地修改一个序列,QtConcurrent::mapped() 返...

weixin_33928467的博客 500

并发编程--- 并发容器(除ConcurrentHashMap )与阻塞队列

ConcurrentSkipListMap 和 ConcurrentSkipListSet 相当于 TreeMap 和 TreeSet 有序的容器,这两种容器的并发版本 SkipList(跳表): 以空间换时间,在原链表的基础上形成多层索引,但是某个节点在插入时,是否成为索引,随机决定,所以跳表又称为概率数据结构。 ConcurrentLinkedQueue LinkedList的并发版本。 无界非阻塞队列,底层是个链表,遵循先进先出原则。add,offer将元素插入到尾部,peek(拿头部的数据,但是不

jym的博客 693

阻塞Map BlockingMap的实现

做socket应用用到了BlockingQueue接口,可用于生产者消费者模式,多个线程阻塞着等待queue的数据到来,但是如果是该线程需要等待某个特定的数据该如何处理呢,自己写了个BlockingMap [code=&quot;java&quot;] public interface BlockingMap { public void put(Integer key, V o) throws Inte...

songsong 1726

一文彻底搞懂并发容器(ConcurrentHashMap、BlockingQueue等)

并发容器预备知识hashHashMap为什么会出现ConcurrentHashMapJDK1.7中多线程下HashMap死循环分析 预备知识 hash 就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入...

日有寸进 2397

文件系统的两种文件索引模式extent和blockmap

前言:extent和blockmap是张宇的描述方法,可能与其他资料上的不完全一致,按意思    对应即可     任何一种成熟、可靠的文件系统,其索引机制都是相当核心的技术实现。当然,此处并不包括像CDFS,tar之类(不能支持写操作的)的打包型文件系统。仅仅因为需要支持写操作,文件系统的设计上就需要顾虑太多,对于任何一个文件,可能会随时增加或减少内容,而伴随着容量的改变,磁盘空间的分布、文件...

weixin_34246551的博客 990

BlocksMap结构

之前看了HDFS代码,做了一些整理,陆续贴上来,Namenode中最重要的结构是Namespace和BlocksMap,先把BlocksMap写一下。 BlocksMap里面最重要的一个结构是BlocksInfo,先来看一下BlocksInfo 1. Class BlocksInfo extends Block Block类有三个变量,分别是long blockId; ...

云端计算 649

BlocksMap源码分析

BlocksMap,主要功能是保存block和其元数据的映射,block的元数据为它所在的INode和存储该block的DataNode。Block对象记录了blockid、block大小以及时间戳信息。block->DataNode的信息没有持久化存储,而是NameNode通过DataNode的blockReport获取该block所在的DataNode List。block的元数据对应的类为B

I am not a quitter. 2661
上一篇: 庖丁解牛式讲解并发编程!GitHub高星阿里新开源Java进阶必备手册
下一篇: 这份Java面试八股文让329人成功进入大厂,堪称2023最强
Java技术那些事儿
博客等级 码龄5年 2306粉丝 2584原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值