`
sixsun
  • 浏览: 9147 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

DelayQueue与Leader-Follwer

 
阅读更多
  •  技术特性

        DelayQueue是JDK并发包中提供的一个容器类,顾名思义这个队列提供了一种延迟机制。

  • 容器作用

        DelayQueue容器中存储的元素具有时效性,该容器需要和实现了Delayed接口的元素配合使用。Delayed类型的元素具有一个失效时长,即这个对象将在多长时间内过期。Delayed接口中提供了一个getDelay方法来获取这个时长,当返回的时长为0或为负数时表示这个对象已经过期了。Delayed接口还实现了一个Comparable接口,有助于它在容器中实现排序。

     代码片段 Delayed接口

public interface Delayed extends Comparable<Delayed> {
     /**
      * 返回该对象将要时效的时间,unit为时间单位。
      */
     long getDelay(TimeUnit unit);
}

 

 

  • 数据结构

        DelayQueue容器内部使用了一个PriorityQueue (队列中的元素具有一定的优先级)来实现的。

  • 容器性质

        只有在延迟期满时才能从中提取出元素。该队列的头部是延迟期满后保存时间最长的 Delayed 元素。如果延迟都还没有期满,则队列头部不会被返回,即队列的 poll 方法将返回null。

  • 容器边界

        容器是一个无界阻塞队列。但此队列不允许使用 null 元素。

  • 常用方法

        public int size()


        返回此 collection 中的元素数,包含已经到期的元素和未到期的元素。如果此容器包含的元素大于 Integer.MAX_VALUE,则返回 Integer.MAX_VALUE。

  • 阻塞方法

        public E take()


        如果该队列的头部元素已经过期则立即返回,如果该队列的头部无法立刻得到一个过期的元素。否则该方法将一直等待,直到获得一个过期的元素。

        public E poll(long timeout, TimeUnit unit) throws InterruptedException


        如果该队列的头部元素已经过期则立即返回,如果该队列的头部无法立刻得到一个过期的元素。否则该方法会在指定的timeout时间内等待,直到得到一个过期的元素。如果在指定的timeout的时间到达后依然没有元素过期,则返回null。

  • 非阻塞方法

        public E poll()


        如果该队列的头部元素已经过期则立即返回,如果没有过期元素则立即返回null。该方法不会被阻塞。

        public boolean offer(E e, long timeout, TimeUnit unit)


        将指定的元素插入此延迟队列中。虽然该方法提供了timeout和util参数,会让人误以为这个方法会被阻塞。但由于该队列是无界的,所以此方法不会阻塞。


        DelayQueue类实现了BlockingQueue接口,这个方法由BlockingQueue接口得到,因此必须实现。该实现忽略了两个与超时相关的参数,而直接调用无参数的offer方法。

代码片段 DelayQueue的offer方法

public boolean offer(E e, long timeout, TimeUnit unit) {
    return offer(e);
}

                      
        public boolean remove(Object o)

 


        从该队列中删除指定的元素(如果存在),而无需关心这个元素是否有过期。

        public void clear()


        移除此延迟队列中的所有元素。该方法无需等待队列中的元素过期。DelayQueue的clear方法实际上是委托PriorityQueue的clear方法实现的,直接将队列中的元素置空。


代码片段 PriorityQueue的clear方法实现

public void clear() {
        modCount++;
        for (int i = 0; i < size; i++)
            queue[i] = null;
        size = 0;
}

 

 

  • 迭代方法

        public Iterator<E> iterator()


        返回在此队列中的所有元素(既包括到期的,也包括未到期的)上进行迭代的迭代器。迭代器不以任何特定的顺序返回元素。允许并发修改。该方法不会抛出 ConcurrentModificationException,即该迭代器在迭代元素的过程中不会去检测队列是否有被其他线程修改。所谓修改即队列中元素的入队、出队或移除操作所带来的元素增减变化。


        弱一致迭代器。该迭代器只能反映出iterator方法在创建该迭代器时队列中元素的情况,而不能反映出创建该迭代器之后队列所发生的元素改变,即队列中元素的入队、出队或移除操作所带来的元素增减变化,该迭代器是一无所知的。

 

  • DelayQueue与Leader-Follwer模式

        代码分析是非常无聊的事情,代码的实现来自人类的一般思考或对事物的语言描述,这里通过一个通俗的业务场景来描述DelayQueue中的Leader-Follwer模式实现,其中各种场景问题思考都与代码实现是一一对应的,如对代码实现感兴趣就看看代码,不感兴趣知道怎么用就行了。

        

         业务背景是酒店门口的出租车等候乘客与乘客候车。出租车不允许插队或以无次序的方式拉客的,一般情况下是一辆车跟在另一辆车后面,等前面的车走了下一辆车再拉客(但也会有例外);候车的乘车也是在候车站台基于某种有序的方式排队等车。在候车和拉客的过程中将会面临如下场景和问题:


take方法需要面临的场景


          take方法好像酒店门口的出租车进入候车口载走乘客

 

        场景1:

 

出租车A来到宾馆门口的候车口
出租车司机A看到宾馆门口没有一个人
出租车司机A马上离开宾馆门口的候车口

 

 


        场景2:

出租车司机A来到宾馆门口的候车口,看到有乘客1在候车
出租车司机A问乘客1:你要走吗?
乘客1说:马上走
出租车司机A对乘客1说:上车吧。

 

        场景3:

出租车司机A来到宾馆门口的候车口,看到有乘客1在候车
出租车司机A问乘客1:你要走吗?
乘客1说:等我10分钟再走好吗

 

        场景3.1:

出租车司机A在宾馆门口竖了个牌子,表示这个候车口暂时归我独占了
出租车司机A在宾馆门口打了10分钟盹

 

        场景3.2:

出租车司机A在打盹10分钟之后睡醒了

 

        场景3.3......

出租车司机A看了一下宾馆门口的牌子,发现这个牌子是自己之前放的
出租车司机A把自己放的牌子收起来了,表示这个候车口不归我独占了

 

        场景3.4......

出租车司机A对乘客A说:10分钟到了,可以走了,然后把乘客1带走了。

 

        场景4:

出租车司机A来到宾馆门口的候车口,看到有乘客1在候车
出租车司机A问乘客1:你要走吗?
乘客1说:等我10分钟再走好吗

 

        场景4.1......

出租车司机A在宾馆门口竖了个牌子,表示这个候车口暂时归我独占了
出租车司机A在宾馆门口打了10分钟盹,并且上了一个10分钟的闹钟

 

        场景4.2......

出租车司机A打盹的2分钟后,宾馆门口又来了一个出租车司机B
出租车司机B来到宾馆门口的候车口,同样看到有乘客1在候车
出租车司机A问乘客1:你要走吗?
乘客1说:等我8分钟再走好吗

 

        场景4.3......

出租车司机B这时后发现宾馆门口竖了个牌子,说明在他之前有人已经拉了这个活
出租车司机B只好也停止宾馆门口候车区打盹,因为出租车司机A独占了这个候车口

 

        场景4.4......

出租车司机A打盹的10分钟内,陆陆续续来了其他的出租车司机
他们都发现了有乘客1在候车,但也都发现宾馆门口竖了个牌子
此刻出租车司机A还在打盹中,他不知道在他的车后面已经堵了好多其他出租车。
并且出租车司机A后面的司机没有上闹钟,他们现在都长睡不起

 

        场景4.5......

出租车司机A在打盹10分钟之后睡醒了,因为他的闹钟响了
出租车司机A看了一下宾馆门口的牌子,发现这个牌子是自己之前放的
出租车司机A把自己放的牌子收起来了,表示这个候车口不归我独占了

 

        场景4.6......

出租车司机A对乘客A说:10分钟到了,可以走了,然后把乘客1带走了。

 

        场景4.7......

出租车司机A在离开候车口时,不忘又检测了候车口属于自己的牌子收起来没有
出租车司机A又看了下候车口,看看还有没有其他乘客

 

        场景4.7.1......

出租车司机A发现候车口还有一个乘客(但也不清楚这个乘客是不是马上就要走)
出租车司机A对候车区大喊了一声,来活了
堵在候车口的其中一个司机(仅此一个)听到喊声,从睡梦中醒来
出租车司机A从候车区离开

 

        场景4.7.2......

唯一听到出租车司机A喊叫的那个司机X,重新检测候车口,发现有一个乘客2
然后出租车司机X重复场景4.1到场景4.7的过程,期间:
出租车司机X可能会运气很好,乘客2是一个马上就走的乘客,出租车司机X并不会叫醒他后面的出租车司机
出租车司机X也可能运气不好,乘客2也要等上一段时间才走,出租车司机X会像出租车司机A一样成为放一个自己牌子,他成了独占者

 

或者

        场景继续4.7.2......

 

 

出租车司机A发现候车口没有乘客
出租车司机A默默的离开
堵在候车口的其他司机依然在睡觉,不知道出租车司机A已经走了

 
      offer方法需要面临的场景


        offer方法就好像酒店门口的乘客来到候车口乘车离开

 


        场景1:

乘客N来到候车口排队
这个时候可能已经来了很多乘车的乘客,而现在站在这个队伍最前面的人是乘客M,
这个队伍是按照谁着急着走谁站在前面,最着急走的那个乘客会站在队伍的最前面

 

        场景2.1:

刚刚来的乘客N现在是最着急要走了那个人

他会把放在候车口处的牌子拿走(如果有的话)。候车口如果有牌子,说明有一个领头的出租车司机X独占了这个候车位,出租车司机X还在睡梦中等闹铃响,然后希望乘客M坐着他的车离开。
现在乘客N成了最着急要走的那个人,并且乘客N已经站在了队伍的最前面,比乘客M还要靠前。
现在候车口处的牌子已经被乘客N拿开了,出租车司机X已经没有独占候车口的权利了,但出租车司机X自己还不知道
乘客N喊了一声,有车要走吗,堵在候车口的其中一个司机(仅此一个,有可能是出租车司机X,或者不是)听到喊声,从睡梦中醒来,这个时候他将有机会拉乘客N,并重复场景4.1到场景4.7的过程而之前的乘客M会在之后坐车离开,如果又来了比乘客M着急的乘客L,则乘客M还得等乘客L先走

 

  • 问题思考

        思考1:如果此时乘客N不去移除候车口处的牌子(如果有的话),只是喊了一声有车要走吗,会怎么样?

        堵在候车口的其中一个司机(仅此一个,有可能是出租车司机X,或者不是)听到喊声,从睡梦中醒来,这个时候他将有机会拉乘客N。
        如果不是出租车司机X,他醒来以后发现了乘客N,但是他也发现这个候车口依然被最前面的一个出租车司机独占(但他并不知道是那个司机独占了),换句话说,如乘客N不去移除候车口处的牌子,即便有司机想带他走,也会因为出租车司机X独占了候车口,而继续在堵在出租车司机X后面,重新睡觉。

        思考2:为什么乘客N不去唤醒所有(notifyAll)沉睡中的司机,而只是唤醒(notify)其中一个?

        当前乘客N是最有可能马上离开的乘客,而他只需要一位司机为他服务就够了,唤醒所有的司机,没有任何好处,反而造成多个司机因争抢客户而浪费时间。

 

分享到:
评论

相关推荐

    JDK自带的延迟队列-DelayQueue

    **延迟队列(DelayQueue)详解** 在Java的并发编程中,`DelayQueue`是一个非常特殊的队列,它属于并发包`java.util.concurrent`的一部分。`DelayQueue`是一个基于优先级队列(PriorityQueue)实现的无界阻塞队列,...

    DelayQueue延迟队列和Redis缓存实现订单自动取消功能

    将订单信息存储为Redis中的键值对,设置键的过期时间与订单的超时时间一致。当订单到达超时时间后,Redis会自动删除该键,这可以触发一个事件或者通过定期扫描过期键来检测并执行订单取消操作。 具体实现步骤如下:...

    DelayQueue

    学习视频,可以丰富java知识。能够获得更多的专业技能

    DelayQueue的使用以及注意事项

    DelayQueue的使用以及注意事项,这里需要由BlockingQueue的基本知识,一般的Queue的使用方法poll(),take(),drainTo()和offer(),put()这些应该懂。

    DelayQueue、Redis结合使延迟、定时任务使用源代码

    本文将深入探讨如何利用Java中的`DelayQueue`和Redis来实现这一功能。`DelayQueue`是Java并发库`java.util.concurrent`中的一个数据结构,它是一个基于优先级队列的无界阻塞队列,可以用于存储具有延迟时间的元素。...

    springboot执行延时任务之DelayQueue的使用详解

    Spring Boot延时任务之DelayQueue的使用详解 DelayQueue是一个无界阻塞队列,只有在延迟期满时,才能从中提取元素。它提供了在指定时间才能获取队列元素的功能,队列头元素是最接近过期的元素。DelayQueue的元素...

    Java多线程并发开发之DelayQueue使用示例

    Delayed接口的实现必须定义一个compareTo方法,该方法提供与getDelay方法一致的排序。 DelayQueue的应用场景 DelayQueue的应用场景非常广泛,例如: 1. 任务调度:DelayQueue可以用于实现任务调度系统,按照...

    基于DelayQueue的简单的定时任务队列.zip

    基于DelayQueue的简单的定时任务队列.zip Quick Start class Main { public static void main(String[] args) { // 初始化任务队列 JobScheduler scheduler = new JobScheduler("default"); // 向队列中提交任务...

    delay-queue:JDK实现的本地delayQueue和基于分布式Redis的两种分布式

    local delayQueue implemented by JDK & two kinds of distributed delayQueue based redis 1. 基本介绍 RedisSynDelayQueue 基于redis,并发情况下会加分布式锁,单线程场景(syn=false)性能较好, 并发场景性能较...

    Java集合常见面试题总结(上)-JavaGuide面经思维导图总结

    - 扩容机制与`ArrayList`类似。 - **LinkedList** - 底层采用双向链表实现。 - 特点:插入和删除操作快,随机访问慢。 - JDK 1.6之前使用循环链表,JDK 1.7取消循环。 ##### 2. `Set` - **HashSet** - 基于`...

    java利用delayedQueue实现本地的延迟队列

    * 返回与此对象相关的剩余延迟时间,以给定的时间单位表示 */ @Override public long getDelay(TimeUnit unit) { return unit.convert(this.time - System.nanoTime(), TimeUnit.NANOSECONDS); } @Override ...

    基于SpringBoot的延迟消息Starter设计源码,支持DelayQueue、Redisson、RabbitMQ三种方式

    该项目是SpringBoot框架下的延迟消息Starter,提供对DelayQueue、Redisson和RabbitMQ三种延迟消息机制的集成支持。项目包含32个文件,涵盖24个Java源文件、4个XML配置文件、1个Git忽略文件、1个Markdown文件、1个...

    Go-delay-queue基于Redis实现的延迟队列

    源码可能包含了任务的创建、调度、消费等接口,以及与Redis交互的具体实现。同时,它可能还包含了单元测试和示例用例,帮助开发者理解和使用这个延迟队列。 总的来说,这个Go延迟队列项目结合了Go语言的并发优势和...

    php-delayqueue:基于redis实现高可用,易拓展,接入方便,生产环境稳定运行的延迟队列

    1.扫表与数据库长时间连接,在数量量大的情况容易出现连接异常中断,需要更多的异常处理,对程序健壮性要求高 2.在数据量大的情况下延时较高,规定内处理不完,影响业务,虽然可以启动多个进程来处理,这样会带来...

    java.util.concurrent_您不知道的5件事

    - **定义与作用**:`Semaphore` 类是一种控制多个线程访问共享资源的机制,它通过内部维护一个整数计数器(许可的数量)以及一组等待线程来实现。当一个线程调用 `acquire()` 方法时,如果当前可用的许可数量大于0,...

    Java企业版中性能调节的最佳实践.pdf

    - **分析架构与设计**:评估应用是否使用分布式对象(例如EJB)、数据库连接方式、同步或异步调用等。 - **性能术语理解**:了解关键性能指标的含义,比如负载(峰值或平均值)、点击(页面访问或HTTP请求)、响应...

    高级Java人才培训专家-05-延迟队列精准发布文章

    **一、文章定时发布的应用场景与需求** - **背景:** 文章定时发布功能适用于多种场景,如新闻更新、博客发布等。该功能允许作者设定文章的发布时间,即使在非工作时间也能自动发布。 - **需求说明:** - **立即发布...

    Delayed interface and Delay Queue

    2. `int compareTo(Delayed other)`:比较当前对象与另一个Delayed对象的延迟时间,用于DelayQueue的排序。 DelayQueue是一个无界的并发队列,它使用Delayed接口的特性来存储和管理元素。这个队列的独特之处在于,...

    Live555 学习笔记

    - 它允许添加、删除和查找与特定Socket相关的处理器(Handler),从而方便地控制网络通信的各个方面。 - **Groupsock**: - `Groupsock`类是Live555中用于封装Socket操作的关键类,尤其在多播(Multicast)和一对...

    rabbitmq延迟插件.zip

    集成Spring Boot与RabbitMQ的第一步是添加相关依赖。在`pom.xml`文件中,我们需要引入Spring Boot的`spring-boot-starter-amqp`依赖以及RabbitMQ的延迟插件。确保以下依赖项已添加: ```xml &lt;groupId&gt;org.spring...

Global site tag (gtag.js) - Google Analytics