手撕定时任务

手撕定时任务

源码:https://gitee.com/bossDuy/hand-tearing-timed-task

我们想要实现的功能:

public class ScheduleService {

    //每隔delay毫秒执行一次task
    void schedule(Runnable task,long delay) {
        
    }
}

那么首先考虑谁去执行这个task?线程池中线程可以吗?

    void schedule(Runnable task, long delay) throws InterruptedException {
        ExecutorService pool = Executors.newFixedThreadPool(5);
        while (true) {
            Thread.sleep(delay);
            pool.execute(task);
        }
    }

但是这样存在问题,我们线程池的大小是5,如果我们第6次调用该方法,也就是调用任务的第6次,就会出现问题

我们考虑一个组件trigger,该组件阻塞delay时间,然后被唤醒的时候将task将给线程池执行,具体如下

package com.yb0os1;

import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.locks.LockSupport;

public class ScheduleService {
    private ExecutorService pool = Executors.newFixedThreadPool(10);
    Trigger trigger = new Trigger();

    public void schedule(Runnable schedule, long delay) {
        Job job = new Job(schedule, System.currentTimeMillis()+delay,delay);
        trigger.jobQueue.offer(job);
        trigger.wakeUp();
    }

    public void schedule(Job job) {
        job.setStartTime(System.currentTimeMillis()+job.getDelay());
        trigger.jobQueue.offer(job);
        trigger.wakeUp();
    }

    //中断任务
    public void interrupt(Job job){
        trigger.jobQueue.removeIf(e->e.equals(job));
    }



    class Trigger {
        private final PriorityBlockingQueue<Job> jobQueue = new PriorityBlockingQueue<>();

        Thread thread = new Thread(() -> {
            while (true) {
                //如果为空 那么阻塞  为了防止虚假唤醒 需要while
                while (jobQueue.isEmpty()) {
                    LockSupport.park();
                }
                //先获取最先要执行的
                Job job = jobQueue.peek();
                if (job.getStartTime()<System.currentTimeMillis()){
                    //任务可以执行
                    //这里一次peek一次poll为了防止执行延迟为1s的任务的时候 插入了延迟为500ms的任务 需要先执行延迟为500ms的任务
                    job = jobQueue.poll();
                    pool.execute(job.getTask());
                    //我们是定时任务 执行了这次还要计算下次要执行的时间点
                    Job nextJob = new Job(job.getTask(),System.currentTimeMillis()+job.getDelay(),job.getDelay());
                    jobQueue.offer(nextJob);
                }else{
                    //最近要执行的任务都不可以执行 延迟等待到开始时间
                    //如果等待的时候添加了一个任务 那么会被唤醒 重新走一遍逻辑
                    LockSupport.parkUntil(job.getStartTime());
                }
            }
        });
        {
            thread.start();
        }
        public void wakeUp() {
            LockSupport.unpark(thread);
        }
    }
	//Job就是我们的执行的任务对象 包括如下
    static class Job implements Comparable<Job>{
        private Runnable task;
        private long startTime;
        private long delay;

        public void setStartTime(long startTime) {
            this.startTime = startTime;
        }

        public Job(Runnable task, long delay){
            this.task = task;
            this.delay = delay;
        }
        public Job(Runnable task, long startTime, long delay) {
            this.task = task;
            this.startTime = startTime;
            this.delay = delay;
        }

        public long getStartTime() {
            return startTime;
        }

        public Runnable getTask() {
            return task;
        }

        public long getDelay() {
            return delay;
        }

        public int compareTo(Job o) {
            return Long.compare(this.startTime,o.getStartTime());
        }

        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Job job = (Job) o;
            return delay == job.delay && Objects.equals(task, job.task);
        }

        public int hashCode() {
            return Objects.hash(task, delay);
        }
    }
}

测试

package com.yb0os1;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        ScheduleService scheduleService = new ScheduleService();
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("mm:ss SSS");
        ScheduleService.Job job = new ScheduleService.Job(() -> System.out.println(LocalDateTime.now().format(dateTimeFormatter) + "--100ms一次的任务"),100);
        scheduleService.schedule(job);
        Thread.sleep(1000);
        scheduleService.interrupt(job);
        scheduleService.schedule(() -> System.out.println(LocalDateTime.now().format(dateTimeFormatter) + "--200ms一次的任务"), 200);

    }
}

在这里插入图片描述

是有几十毫秒的误差,这是正常的

  • LockSupport.parkUntil() 的精度限制

  • 使用 PriorityBlockingQueue 实现优先级队列带来的额外开销

  • 多线程协作带来的额外延迟

    • schedule() --> Trigger.jobQueue.offer() --> LockSupport.unpark() --> Thread 被唤醒 --> poll 并执行任务
      

我们就算使用JDK提供的标准定时任务工具类ScheduledExecutorService

    public static void main(String[] args) throws InterruptedException {
//        ScheduleService scheduleService = new ScheduleService();
//        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("mm:ss SSS");
//        ScheduleService.Job job = new ScheduleService.Job(() -> System.out.println(LocalDateTime.now().format(dateTimeFormatter) + "--100ms一次的任务"),100);
//        scheduleService.schedule(job);
//        Thread.sleep(1000);
//        scheduleService.interrupt(job);
//        scheduleService.schedule(() -> System.out.println(LocalDateTime.now().format(dateTimeFormatter) + "--200ms一次的任务"), 200);
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
        pool.scheduleAtFixedRate(() -> System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("mm:ss SSS")) + "--100ms一次的任务"), 100, 100, java.util.concurrent.TimeUnit.MILLISECONDS);
    }

在这里插入图片描述

### 动实现 Transformer 模型进行机器翻译 Transformer 是一种基于自注意力机制的深度学习模型,广泛用于自然语言处理任务中的序列到序列建模。以下是关于如何动实现 Transformer 模型以完成翻译任务的具体方法。 #### 1. 数据预处理 数据预处理阶段通常涉及分词、编码以及创建词汇表等操作。对于源语言和目标语言的数据集,可以采用如下方式准备输入: - 使用 `torchtext` 或其他工具对文本进行标记化。 - 构造源语言和目标语言的词汇表,并将其转换为整数索引表示。 - 增加特殊标记(如 `<PAD>`、`<SOS>` 和 `<EOS>`)以便填充短句或指示句子起始/结束位置[^1]。 #### 2. 编码器 (Encoder) 的设计 编码器部分接收输入序列并通过多头注意力机制提取特征向量。其主要组件包括嵌入层、位置编码、多头注意力模块及前馈网络: - **Embedding Layer**: 将单词映射成固定维度的连续空间向量。 - **Positional Encoding**: 添加绝对或相对的位置信息给嵌入向量,因为 transformer 不具备内在顺序感知能力。 - **Multi-head Attention Mechanism**: 并行计算多个子空间内的点积注意力权重来捕捉不同方面的关系。 - **Feed Forward Network**: 对每个时间步应用两层全连接神经网络进一步变换特征[^3]。 ```python import math import torch from torch import nn class PositionalEncoding(nn.Module): def __init__(self, d_model, max_len=5000): super(PositionalEncoding, self).__init__() pe = torch.zeros(max_len, d_model) position = torch.arange(0, max_len).unsqueeze(1) div_term = torch.exp(torch.arange(0, d_model, 2) * -(math.log(10000.0) / d_model)) pe[:, 0::2] = torch.sin(position * div_term) pe[:, 1::2] = torch.cos(position * div_term) pe = pe.unsqueeze(0) self.register_buffer('pe', pe) def forward(self, x): return x + self.pe[:, :x.size(1)] ``` #### 3. 解码器 (Decoder) 的结构 解码器同样由若干重复堆叠的子层构成,区别在于引入了掩蔽多头注意力建立因果约束防止未来信息泄露;此外还存在跨语言交互的部分负责融合来自 encoder 输出的内容上下文线索[^4]: - Masked Multi-Head Self-Attention: 防止当前时刻看到后续 token 的情况发生。 - Encoder-Decoder Attention: 结合先前生成的结果与 source side 表达共同决定下一步产出什么内容最为合适。 #### 4. 训练过程概述 定义损失函数一般选取交叉熵作为衡量标准,在每一轮迭代过程中优化参数直至收敛为止。具体步骤可能包含以下几个方面: - 初始化所有可训练变量随机赋初值; - 输入一批次样本至 network 中得到预测分布概率 P(y|x); - 根据真实标签 y* 调整误差反传更新 weights/biases 参数集合[^2]。 ```python criterion = nn.CrossEntropyLoss(ignore_index=pad_idx) optimizer = torch.optim.Adam(model.parameters(), lr=lr, betas=(0.9, 0.98), eps=1e-9) for epoch in range(num_epochs): model.train() total_loss = 0. for batch in train_iter: src, tgt = batch.src, batch.trg optimizer.zero_grad() output = model(src, tgt[:-1]) loss = criterion(output.view(-1, vocab_size), tgt[1:].reshape(-1)) loss.backward() optimizer.step() total_loss += loss.item() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值