1. DelayQueue 延时队列
DelayQueue 是 Java 并发包 java.util.concurrent 下的一个线程安全的阻塞队列,它存储的元素必须实现 Delayed 接口,以便计算元素的延时时间。队列中的元素只有在其指定的延迟时间到达之后才能从队列中取出。
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
public class Order implements Delayed {
private long time;
private String name;
public Order(String name, long delay, TimeUnit unit) {
this.name = name;
this.time = System.currentTimeMillis() + unit.toMillis(delay);
}
@Override
public long getDelay(TimeUnit unit) {
long delay = time - System.currentTimeMillis();
return unit.convert(delay, TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed other) {
if (this.time < ((Order) other).time) {
return -1;
} else if (this.time > ((Order) other).time) {
return 1;
}
return 0;
}
}
public class DelayQueueDemo {
public static void main(String[] args) throws InterruptedException {
DelayQueue<Order> delayQueue = new DelayQueue<>();
delayQueue.put(new Order("Order1", 5, TimeUnit.SECONDS));
delayQueue.put(new Order("Order2", 10, TimeUnit.SECONDS));
delayQueue.put(new Order("Order3", 15, TimeUnit.SECONDS));
System.out.println("订单延迟队列开始时间:" + java.time.LocalDateTime.now());
while (delayQueue.size() != 0) {
Order order = delayQueue.take(); // 阻塞直到元素可用
System.out.format("订单: %s 被取消, 取消时间: %s\n", order.name, java.time.LocalDateTime.now());
}
}
}
2. Quartz 定时任务
Quartz 是一个开源的任务调度库,可以集成到几乎任何Java应用中,用于定时执行任务。通过定义任务和触发器,可以很容易地实现定时任务。
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
public class QuartzJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
System.out.println("执行定时任务: " + System.currentTimeMillis());
}
}
// 在 Spring 配置文件中配置 Quartz
// ...
3. Redis sorted set
Redis 的有序集合(sorted set)可以利用 score 来实现延时队列。通过设置元素的 score 为过期时间戳,可以实现在特定时间自动过期并被消费。
import redis.clients.jedis.Jedis;
public class RedisDelayQueue {
private static final String DELAY_QUEUE = "delayQueue";
public void addToQueue(String key, long delaySeconds) {
double score = System.currentTimeMillis() / 1000 + delaySeconds;
new Jedis().zadd(DELAY_QUEUE, score, key);
}
public void consume() {
long now = System.currentTimeMillis() / 1000;
while (true) {
Set<String> keys = new Jedis().zrangeByScore(DELAY_QUEUE, 0, now);
for (String key : keys) {
new Jedis().zrem(DELAY_QUEUE, key);
System.out.println("消费元素: " + key);
}
if (keys.isEmpty()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
4. Redis 过期回调
Redis 可以配置过期事件通知,当一个键过期时,Redis 会发送一个事件通知给订阅了该事件的客户端。
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
public class RedisKeyExpirationListener implements MessageListener {
@Override
public void onMessage(Message message, byte[] pattern) {
String expiredKey = new String(message.getBody());
System.out.println("监听到key:" + expiredKey + "已过期");
}
}
// 在 Spring 配置中配置 RedisMessageListenerContainer
// ...
5. RabbitMQ 延时队列
RabbitMQ 通过消息的 TTL(Time To Live)和死信交换机(DLX)来实现延时队列。
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.core.Message;
public class RabbitMQDelayQueue {
private final RabbitTemplate rabbitTemplate;
public RabbitMQDelayQueue(RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
}
public void sendDelayedMessage(String message, long delay) {
Message msg = new Message(message.getBytes(), new MessageProperties() {
{
setExpiration(String.valueOf(delay));
// 设置消息的其他属性
}
});
rabbitTemplate.send("delayQueueExchange", "delayQueueRoutingKey", msg);
}
// 配置交换机、队列和绑定
// ...
}
6. 时间轮算法
时间轮算法是一种高效的定时任务管理算法,Netty 提供了 HashedWheelTimer 来实现时间轮。
import io.netty.util.HashedWheelTimer;
import io.netty.util.Timer;
import io.netty.util.TimerTask;
public class NettyDelayQueue {
public static void main(String[] args) {
Timer timer = new HashedWheelTimer();
timer.newTimeout(new TimerTask() {
@Override
public void run(Timeout timeout) throws Exception {
System.out.println("任务执行: " + System.currentTimeMillis());
}
}, 5, TimeUnit.SECONDS);
}
}