Backend Development 17 min read

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.

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

login 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.