Implementation and Comparison of Delayed Task Solutions in Java

This article explains the concept of delayed tasks, compares them with scheduled tasks, and presents five implementation approaches—database polling, JDK DelayQueue, time‑wheel algorithm, Redis sorted sets, and message‑queue based delay queues—detailing their code, advantages, and drawbacks.

Top Architect
Top Architect
Top Architect
Implementation and Comparison of Delayed Task Solutions in Java

Introduction

In development, delayed tasks such as automatically cancelling an unpaid order after 30 minutes or sending an SMS 60 seconds after order creation are common. A delayed task differs from a scheduled task in that it has no fixed trigger time, no execution cycle, and usually handles a single job.

Solution Analysis

(1) Database Polling

Idea : A single thread periodically scans the database for overdue orders and updates or deletes them. Suitable for small projects.

Implementation (using Quartz):

<dependency>
  <groupId>org.quartz-scheduler</groupId>
  <artifactId>quartz</artifactId>
  <version>2.2.2</version>
</dependency>

Demo class MyJob:

package com.rjzheng.delay1;

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class MyJob implements Job {
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("Scanning database...");
    }
    public static void main(String[] args) throws Exception {
        JobDetail jobDetail = JobBuilder.newJob(MyJob.class)
            .withIdentity("job1", "group1").build();
        Trigger trigger = TriggerBuilder.newTrigger()
            .withIdentity("trigger1", "group3")
            .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                .withIntervalInSeconds(3).repeatForever())
            .build();
        Scheduler scheduler = new StdSchedulerFactory().getScheduler();
        scheduler.scheduleJob(jobDetail, trigger);
        scheduler.start();
    }
}

Output shows "Scanning database..." every 3 seconds.

Pros : Simple, supports clustering. Cons : High memory consumption, latency depends on scan interval, heavy DB load for large data sets.

(2) JDK DelayQueue

Idea : Use the unbounded blocking queue DelayQueue where elements become available only after their delay expires.

Implementation of OrderDelay (implements Delayed) and a demo that enqueues five orders with a 3‑second delay:

package com.rjzheng.delay2;

import java.util.concurrent.*;

public class OrderDelay implements Delayed {
    private String orderId;
    private long timeout;
    OrderDelay(String orderId, long timeout) {
        this.orderId = orderId;
        this.timeout = timeout + System.nanoTime();
    }
    public int compareTo(Delayed other) { /* omitted for brevity */ }
    public long getDelay(TimeUnit unit) {
        return unit.convert(timeout - System.nanoTime(), TimeUnit.NANOSECONDS);
    }
    void print() { System.out.println(orderId + " order expired..."); }
}

Demo DelayQueueDemo prints each order after 3 seconds.

Pros : High efficiency, low trigger latency. Cons : Data lost on server restart, difficult to scale in a cluster, possible OOM with many pending orders.

(3) Time‑Wheel Algorithm

Idea : Use a circular timer wheel (e.g., Netty's HashedWheelTimer) where each tick represents a time slice; tasks are placed into slots based on their delay.

Dependency:

<dependency>
  <groupId>io.netty</groupId>
  <artifactId>netty-all</artifactId>
  <version>4.1.24.Final</version>
</dependency>

Demo HashedWheelTimerTest schedules a task after 5 seconds and prints elapsed seconds.

Pros : High efficiency, lower latency than DelayQueue, simpler code. Cons : Same persistence issues as DelayQueue, not easy to cluster.

(4) Redis Sorted Set

Idea : Store order IDs as members and their expiration timestamps as scores in a Redis ZSET. Periodically fetch the smallest score and process if overdue.

Key commands: ZADD key score member – add element ZRANGE key 0 -1 WITHSCORES – query ordered elements ZREM key member – remove element

Implementation AppTest shows a producer adding five orders with a 3‑second delay and a consumer that checks the score, removes the element atomically, and processes it.

Pros : Data persisted in Redis, easy horizontal scaling, accurate timing. Cons : Requires Redis maintenance.

Improving Concurrency

To avoid multiple consumers processing the same order, check the return value of ZREM; only when it returns >0 should the order be processed.

(5) Message Queue (RabbitMQ)

Idea : Use RabbitMQ's message TTL ( x-message-ttl) and dead‑letter exchange to implement delayed delivery. When a message expires, it is routed to a dead‑letter queue for processing.

Pros : High efficiency, built‑in clustering, message persistence for reliability. Cons : Additional operational complexity and cost due to RabbitMQ management.

Conclusion

The article compares five delayed‑task solutions, summarizing their strengths and weaknesses, helping developers choose the most suitable approach for their scenario.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaredisMessage Queuedelayed tasksQuartzDelayQueue
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.