Various Implementations of Delayed Tasks in Java: Quartz, DelayQueue, HashedWheelTimer, Redis, and RabbitMQ
This article compares several approaches to implementing delayed tasks in Java, including Quartz polling, JDK DelayQueue, Netty's HashedWheelTimer, Redis sorted sets and keyspace notifications, and RabbitMQ delayed queues, analyzing their principles, code examples, advantages, and drawbacks for order timeout handling.
In many backend systems we need to execute actions after a certain delay, such as automatically cancelling unpaid orders or sending reminder messages.
Delayed tasks differ from scheduled tasks: a delayed task has no fixed trigger time and is executed once after an event, while a scheduled task has a predetermined time and may repeat.
1. Database polling (Quartz) – A simple solution for small projects where a dedicated thread periodically scans the database for overdue orders. Example Maven dependency and a Quartz job:
<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.2</version> </dependency>
public class MyJob implements Job { public void execute(JobExecutionContext context) { System.out.println("Scanning database for overdue orders..."); } }
Pros: easy to implement, supports clustering. Cons: high memory consumption, latency depends on scan interval, heavy DB load under large order volumes.
2. JDK DelayQueue – Uses the built‑in java.util.concurrent.DelayQueue which holds elements that become available only after their delay expires. Sample element and demo:
public class OrderDelay implements Delayed { private String orderId; private long timeout; // constructor, compareTo, getDelay, print() ... }
DelayQueue<OrderDelay> queue = new DelayQueue<>(); queue.put(new OrderDelay("0001", 3, TimeUnit.SECONDS)); OrderDelay od = queue.take(); od.print();
Pros: high efficiency, low latency. Cons: data lost on server restart, OOM risk with massive pending orders, higher code complexity.
3. Time wheel algorithm (Netty HashedWheelTimer) – Models a clock where each tick moves a pointer; tasks are placed into slots based on their delay. Example:
static class MyTimerTask implements TimerTask { public void run(Timeout timeout) { System.out.println("Deleting order after delay..."); } } Timer timer = new HashedWheelTimer(); timer.newTimeout(new MyTimerTask(), 5, TimeUnit.SECONDS);
Pros: efficient, lower latency than DelayQueue, simpler code. Cons: similar durability issues as DelayQueue, limited to in‑memory.
4. Redis sorted set (ZSET) – Stores order IDs as members with the expiration timestamp as the score. Producer adds orders with ZADD , consumer polls the smallest score and removes it with ZREM . Sample code:
Jedis jedis = jedisPool.getResource(); jedis.zadd("OrderId", futureTimestamp, "OID0001"); Set<Tuple> items = jedis.zrangeWithScores("OrderId", 0, 0); if (!items.isEmpty()) { Tuple t = items.iterator().next(); if (System.currentTimeMillis()/1000 >= t.getScore()) { Long removed = jedis.zrem("OrderId", t.getElement()); if (removed != null && removed > 0) { System.out.println("Consumed order " + t.getElement()); } } }
Pros: simple, supports persistence, easy clustering. Cons: requires extra Redis maintenance, concurrency issues solved by checking ZREM return value.
5. Redis keyspace notifications – Configures Redis to publish an expired event when a key’s TTL ends. A subscriber receives the event and processes the order. Example:
public class RedisSub extends JedisPubSub { @Override public void onMessage(String channel, String message) { System.out.println("Order cancelled: " + message); } } new Thread(() -> jedis.subscribe(new RedisSub(), "__keyevent@0__:expired")).start(); jedis.setex("OID0001", 3, "OID0001");
Pros: high accuracy, automatic expiration handling, easy horizontal scaling. Cons: relies on Redis Pub/Sub which is fire‑and‑forget; events are lost if the subscriber disconnects.
6. RabbitMQ delayed queue – Uses message TTL ( x-message-ttl ) and dead‑letter exchange to re‑route expired messages, effectively creating a delay queue. No code is shown, but the principle is described.
Pros: efficient, supports persistence, leverages RabbitMQ’s clustering. Cons: adds operational complexity and dependency on RabbitMQ.
Overall, each solution presents trade‑offs between performance, reliability, and operational overhead; the appropriate choice depends on the specific requirements of the system.
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.