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.
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.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.