DelayQueue实现延迟任务

文章介绍了Java中的DelayQueue,一种用于存储延迟执行任务的有序队列,通过`Delayed`接口和`Comparable`实现按延迟时间排序。示例中模拟用户登录系统,当用户在线时间超过4秒时统计。
摘要由CSDN通过智能技术生成

涉及的类

Delay

DelayQueue的泛型,队列中的对象必须实现Delay接口

简介

1.延迟后执行操作的泛型接口

延迟后执行操作:不会立即执行,而是会等待延迟时间后才会执行

2.实现了Comparable接口,用于排序,排序的规则为延迟时间

3.getDelay()方法用于获取延迟执行的时间或者标记位,简单来说就是方法返回0或者小于0的数,则表示可以执行

DelayQueue

简介

1.长度没有限制

2.元素只有在延迟过期时才会被占用

将队列中加入一个元素并不会占用队列的位置,也就不能进行队列相关操作;但是它们会被视为正常元素,例如,size方法返回已过期和未过期元素的计数,通过peek可以访问元素

!!简单来说,只有到延迟的时间之后,才会被放到队列中,才能进行所有的队列相关操作。否则只能查看,不能移除。

 3.队列的头是延迟在过去过期时间最长的延迟元素。如果没有延迟到期,则没有头,轮询将返回null。

此队列为有序队列,延迟剩余时间越少的排在最前面

4.当元素的getDelay(TimeUnit.NASECONDS)方法返回小于或等于零的值时,延迟到期,可以正式操作元素。

队列相关操作需要查阅相关资料

示例

思路:

1.模拟用户登录系统,当用户停留在系统超过4秒(判断用户线程是否存活或者检查用户Session或Token)。

2.对当前用户进行延迟统计。

VerifyDelay

模拟用户线程

1.用户来的时候创建,并设置过期时间(毫秒)

2.创建后,由于该对象实现了Runnable接口,可以作为线程执行

3.线程执行后,对象中保存当前线程对象

4.通过当前线程对象判断用户是否还活跃,返回用户的名称

package com.zhf.model.thread.juc.collections.queue.test;

import lombok.Data;
import org.jetbrains.annotations.NotNull;
import java.util.Random;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

/**
 * @Author: Haiven
 * @Time: 2024/4/16 14:49
 * @Description: TODO
 */
@Data
public class VerifyDelay implements Delayed,Runnable {

    /** 当前线程对象 */
    private Thread currThread;

    /** 用户名 */
    private String username;

    /** 过期时间 */
    private long expires;

    public VerifyDelay( String username, long expires) {
        this.username = username;
        this.expires = expires + System.currentTimeMillis();
    }

    @Override
    public void run() {
        //模拟用户线程执行操作
        // 保存当前线程对象,用于判断用户状态
        currThread = Thread.currentThread();
        //使用随机数,模拟用户在系统的停留时间
        int i = new Random().nextInt(10);
        try {
            TimeUnit.SECONDS.sleep(i);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        //用户执行信息(执行时间和用户名)
        System.out.println("模拟用户线程执行相关操作:用户名 = { "+ username +" }, 执行时间 = {" + i + "}");
    }

    public String statUsername(){
        //判断用户线程是否存活,存活则返回用户名, 不存活则返回null
        if(currThread.isAlive()){
            return username;
        }else {
            return null;
        }
    }

    @Override
    public long getDelay(@NotNull TimeUnit unit) {
        //过期时间 - 当前系统时间  大于0表示没有过期, 小于/等于0表示过期
        return unit.convert(expires - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
    }

    @Override
    public int compareTo(@NotNull Delayed o) {
        //按照过期时间排序
        long compare = getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS);
        if(compare == 0){
            return 0;
        }
        if(compare > 0){
            return  1;
        }else {
            return -1;
        }
    }
}

MainTest

queue.take():为队列的阻塞操作,如果队列为空则阻塞知道队列中有元素为止

package com.zhf.model.thread.juc.collections.queue.test;

import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * @Author: Haiven
 * @Time: 2024/4/16 15:12
 * @Description: TODO
 */
public class MainTest {

    /**
     * 设置过期时间为 4s, 当用户停留超过4s则统计
     */
    private static final long EXPIRE = 4000L;

    /**
     * 模拟的用户线程数
     */
    private static final int THREAD_COUNT = 8;

    public static List<String> statUserName() throws InterruptedException {
        ArrayList<String> userList = new ArrayList<>();
        BlockingQueue<VerifyDelay> queue = new DelayQueue<>();
        // 模拟用户访问
        for (int i = 0; i < THREAD_COUNT; i++) {
            VerifyDelay userThread = new VerifyDelay("用户 - " + i, EXPIRE);
            queue.offer(userThread);
            // 创建线程执行用户操作
            Thread thread = new Thread(userThread);
            thread.start();
        }

        for (int i = 0; i < THREAD_COUNT; i++) {
            // 获取队列的头对象并返回,如果队列为空则阻塞
            VerifyDelay delay = queue.take();
            // 获取用户名,线程不存活则返回为空
            String username = delay.statUsername();
            if(StringUtils.isNotBlank(username)){
                userList.add(username);
            }
        }
        return userList;

    }

    public static void main(String[] args) throws InterruptedException {
        List<String> result = statUserName();
        //在这里main线程睡10s防止main线程跑的太快,用户线程还没跑完, 不睡也可以,就是打印的比较乱
        TimeUnit.SECONDS.sleep(10);
        System.out.println("==============超过4s的用户===============");
        result.forEach(System.out::println);
    }
}

执行结果

模拟用户线程执行相关操作:用户名 = { 用户 - 1 }, 执行时间 = {0}
模拟用户线程执行相关操作:用户名 = { 用户 - 5 }, 执行时间 = {1}
模拟用户线程执行相关操作:用户名 = { 用户 - 3 }, 执行时间 = {5}
模拟用户线程执行相关操作:用户名 = { 用户 - 4 }, 执行时间 = {7}
模拟用户线程执行相关操作:用户名 = { 用户 - 7 }, 执行时间 = {7}
模拟用户线程执行相关操作:用户名 = { 用户 - 0 }, 执行时间 = {8}
模拟用户线程执行相关操作:用户名 = { 用户 - 6 }, 执行时间 = {9}
模拟用户线程执行相关操作:用户名 = { 用户 - 2 }, 执行时间 = {9}
==============超过4s的用户===============
用户 - 0
用户 - 2
用户 - 4
用户 - 3
用户 - 6
用户 - 7

补充说明

由于系统执行代码片段需要时间(虽然很短),当用户执行时间刚好为4s的时候会有一定的毫秒差,导致统计失败

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kafka实现延迟任务的方法有多种。其中一种常见的方法是使用Kafka的延迟队列功能。在这种方法中,我们可以将任务消息发送到Kafka的一个特定主题,并为该消息设置一个延迟时间。当消息到达延迟时间后,Kafka会将消息推送到消费者进行处理。 具体的实现步骤如下: 1. 首先,我们需要创建一个Kafka的生产者,并配置好相关的参数,包括连接到Kafka集群的地址、序列化器等。 2. 接下来,我们可以定义一个任务对象,将任务的相关数据封装成一个消息对象。 3. 将任务消息发送到Kafka的延迟队列主题中,可以使用Kafka的Producer API提供的send方法。 4. 在消费者端,我们可以创建一个Kafka的消费者,并订阅延迟队列主题。 5. 当消费者接收到延迟队列中的消息时,可以根据消息中的任务数据执行相应的操作。 需要注意的是,Kafka的延迟队列功能是通过使用定时器、DelayQueue和ScheduledThreadPool等机制来实现的。而相比其他实现方式,Kafka的实现方式更加高效和可靠,因为它采用了槽的方式来管理延迟消息,避免了空推进的问题。 综上所述,Kafka可以通过设置延迟时间和使用延迟队列功能来实现延迟任务。这种方式在大数据技术生态中得到了广泛应用,并且具有高效和可靠的特点。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [kafka 延时消息处理](https://blog.csdn.net/cxu123321/article/details/108011694)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [大数据开发:Kafka延迟队列](https://blog.csdn.net/shuimuzh123/article/details/117530975)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [消息队列常见面试题 全解](https://download.csdn.net/download/Wis57/87916787)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值