我太想要1024牌子了,先发一篇不完整的后面再补
先行数据结构
- 大顶堆小顶堆
- 时间轮算法
大顶堆小顶堆
堆是一种特殊的树,只要满足下面的两个条件他就是一个堆
(1)堆是一颗完全二叉树
(2)堆中某个节点的值总是不大于或不小于其父节点
其中,我们把根节点最大的堆叫做大顶堆,根节点最小的为小顶堆
顶堆要求在叶子节点之前的所有节点是一个完全二叉树
堆就是一种完全二叉树,分为小顶堆大顶堆
用这样的数据结构就可以完成取任务时是距离当前时间最近的,直接取堆顶来执行。
存储结构使用数值的形式来存储。这时候找到8的父节点就拿8的位置下标5/2=2,也就是5这个节点的位置,下标除以2就是父节点位置,这样处理是为了后面的堆化。
插入元素
插入尾部,然后上浮
往堆中插入一个元素,我们需要继续满足堆的两个特征
- 堆是完全二叉树
- 堆中某个节点的值总是不大于或不小于其父节点的值
为了满足条件我们把元素插入节点,根据节点进行节点的比较和交换从而保持堆关系
删除堆顶元素
将尾部元素放到堆顶然后下沉
时间轮算法
时间轮顾名思义,就像一个时钟一样将时间划分为12个区间,当走到相应的时间点就触发动作 ,但是存在一个问题。当时间不是12小时而是为24小时,没法区分13点与1点的区别,可以把时间划分为24个,但是这样一个月的的区间就有720个时间区间,同时时间的精度也远远不够只用小时为单位。为了解决这个问题采用分层时间轮算法,将时分秒年月日划分为不同的时间轮,在时间轮的配合之下就可以完成高精度和长时间的时间轮。
JDK定时器:Timer使用及原理
timer的使用
新建Timer类将TimerTask传入到Timer的执行方法中就可实现定时任务
创建时间执行类
public class TimeTest {
public static void main(String[] args) {
Timer timer = new Timer();
for (int i = 0; i < 3; i++) {
TimerTask task=new JobTask();
timer.schedule(task,3000*i);
}
}
}
创建任务执行类
class JobTask extends TimerTask{
@Override
public void run() {
System.out.println("task");
}
}
打印结果
task
task
task
Timer的schedule执行方式是通过判断,下一次要执行的时间与现在的间隔来进行执行,只用在上一个执行完成后下一个才能被执行,如果上个任务超时那么下一个任务就无法正常触发因此此方法存在丢任务的风险。而timer的scheduleAtFixedRate方法是严格按照时间来不是通过计算间隔,所以如果有任务执行时间过长会导致任务的堆积,最后可能会导致任务执行时间乱开。
定时任务线程池
创建线程池,然后进行调用。会发现任务一瞬间完成没有堆积,多线程的情况下会进入异步的状态不会去等待执行时间的到来。直接为所有任务进行定时操作。
public class ScheeduleThreadPoolTest {
public static void main(String[] args) {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
for (int i = 0; i < 3; i++) {
TimerTask jobTask = new JobTask();
scheduledExecutorService.schedule(jobTask,3,TimeUnit.SECONDS);
}
}
}
class JobTask extends TimerTask{
@Override
public void run() {
System.out.println("task");
}
}
Lead-Follow模式
现在又一堆等待执行的任务(一般是存放在一个队列中拍好序),而所有的工作线程中只会一个leader线程,其他线程都是follow线程。只有leader线程能执行任务,而剩下的follow线程则不会去执行任务,他们会处在休眠中的状态。当leader线程拿到任务后执行任务前,同时会选举一个新的leader线程去等待下一个任务,自身然后去执行任务。如果没有任务了会变为休眠状态的follow。
Quartz的使用
Quartz的Job是需要执行的任务,JobDetail是对Job的封装。Scheduler是调度器来进行任务的调度。可以通过StdScheduleFactory Properties来进行配置。DirectSchedulerFactory是通过Api来直接进行配置。Scheduler包括启动停止暂停等操作。
引入pom文件
<!-- Quartz -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<!--mysql 驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.13-SNSAPSHOT</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!--pagehelper分页-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
创建工作类必须为public,因为方法要通过反射调用。
public class JobTask implements Job {
//执行方法
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("quartz");
}
}
Quartz创建任务和触发器,然后传给Scheduler进行任务的调用
public class QuartzDemo {
public static void main(String[] args) {
JobDetail detail = JobBuilder
.newJob(JobTask.class)
.withIdentity("job1", "group1")
.build();
Trigger trigger=TriggerBuilder
.newTrigger()
.withIdentity("trigger1","triggerGroup1").
startNow()
.withSchedule(SimpleScheduleBuilder
.simpleSchedule()
.withIntervalInSeconds(3)
.repeatForever())
.build();
try {
Scheduler defaultScheduler = StdSchedulerFactory.getDefaultScheduler();
defaultScheduler.scheduleJob(detail,trigger);
defaultScheduler.start();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
DetailMap的使用
detailmap可以为定时任务传入参数,在后加入usingJobData可传入键值对作为参数。
public class QuartzDemo {
public static void main(String[] args) {
JobDetail detail = JobBuilder
.newJob(JobTask.class)
.withIdentity("job1", "group1")
.usingJobData("job","detail")
.build();
Trigger trigger=TriggerBuilder
.newTrigger()
.withIdentity("trigger1","triggerGroup1")
.startNow()
.withSchedule(SimpleScheduleBuilder
.simpleSchedule()
.withIntervalInSeconds(3)
.repeatForever())
.build();
try {
Scheduler defaultScheduler = StdSchedulerFactory.getDefaultScheduler();
defaultScheduler.scheduleJob(detail,trigger);
defaultScheduler.start();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
在执行类中可从jobExecutionContext中取出传入的参数,如果JobTask有参数构造方法会自动调用参数的set方法进行参数赋值,如果job和trigger同时赋值,最后会被覆盖trigger有效。
public class JobTask implements Job {
//执行方法
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
Object job = jobExecutionContext.getJobDetail().getJobDataMap().get("job");
System.out.println(job);
}
}
每次执行任务的JobDetail是不同的实例。
案例就到此结束了,我太想要1024牌子了,比较草率。