目录
1.定时器的定义
2.标准库里面定时器代码写法
3.自己实现一个定时器
定时器
-
1.定义
-
相当于闹钟,设定多长时间后执行某个动作.
-
服务器开发过程中,客户端一般会请求服务器.客户端发出请求后,就需要等待服务器响应,但是也不是死等,一般会设置一个时间,超出时间限制不会进行死等.
-
2.标准库里面定时器代码写法
-
一个定时器可以执行多个任务
在这里插入代码片
import java.util.Timer;
import java.util.TimerTask;
public class Demo23 {
public static void main(String[] args) {
Timer timer=new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("时间到快起床");
}
},1000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("时间到快刷牙");
}
},2000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("时间到快洗脸");
}
},3000);
}
}
-
执行上述代码,进程并没有退出,Timer内部需要一组线程来执行任务,而这里的线程是前台线程
前台线程:(main或者new Thread)默认创建的线程都是前台线程,前台线程阻止进程退出
后台线程:不会阻止进程的退出,如果前台线程执行完,程序会立马退出(用setDaemon()设置成后台线程,必须在start调用之前开始设置) -
3.手动创建一个定时器(步骤)
-
先描述里面的任务(执行什么工作,执行的时间)
在这里插入代码片
class Mytask {
//要执行的任务
private Runnable runnable;
//什么时间来执行
private long time;
public Mytask(Runnable runnable, long delay) {
this.runnable = runnable;
//现在的时间
this.time = System.currentTimeMillis()+delay;
}
//因为一会要观察现在时间和设定闹钟时间大小关系,所以要得到这个时间
public Runnable getRunnable() {
return runnable;
}
public long getTime() {
return time;
}
}
- 让MyTimer实现管理多个任务(使用优先级队列和阻塞队列,既可以保证线程安全,也可以管理多个任务,使时间最短的任务先进行)
在这里插入代码片
class MyTimer{
private BlockingQueue<Mytask> queue=new PriorityBlockingQueue<>();
}
- 任务已经放入优先级阻塞队列里面,需要创建扫描线程,让线程不停检查队首元素,检查是否是否到
阻塞队列无法阻塞式取队头元素,这时就需要取出任务,判断时间是否到,时间没有到又塞回队列
在这里插入代码片
lass MyTimer{
private BlockingQueue<Mytask> queue=new PriorityBlockingQueue<>();
//创建一个扫描线程
//写了一个构造方法
public MyTimer(){
Thread t1=new Thread(()->{
while (true){
//取出队头元素
try {
Mytask mytask = queue.take();
//判断当前时间和任务设定时间大小关系
long curTime = System.currentTimeMillis();
if (curTime >= mytask.getTime()) {
//线程运行
mytask.getRunnable().run();
} else {
//把队头元素塞回去
queue.put(mytask);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t1.start();
}
- 因为你的Mytask是自定义类型,需要自己实现比较
在这里插入代码片
class Mytask implements Comparable<Mytask>{
//要执行的任务
private Runnable runnable;
//什么时间来执行
private long time;
@Override
public int compareTo(Mytask o) {
return (int)(this.time-o.time);
}
如果是自定义类型需要比较大小,需要自己实现比较规则
1.实现Comparable接口(只能比较一个方面)
在这里插入代码片
class Student implements Comparable<Student>{
String name;
int age;
@Override
public int compareTo(Student o) {
return this.age-o.age;
}
}
2.实现Comparator接口(可以多方面进行比较)
在这里插入代码片
class Student{
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
class NameComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.age-o2.age;
}
}
class AgeComparator implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return o1.age-o2.age;
}
}
public class Demo26 {
public static void main(String[] args) {
Student student1=new Student("小加",11);
Student student2=new Student("小王",14);
NameComparator nameComparator=new NameComparator();
nameComparator.compare(student1,student2);
AgeComparator ageComparator=new AgeComparator();
ageComparator.compare(student1,student2);
}
}
- 下面这个代码一直在进行忙等,如果不想忙等,就需要使用wait,等其他线程任务进行唤醒
可以进行加锁操作(如果扫描线程先执行take之后,线程切换到shedule线程,新增加一个任务,假设这个任务1点执行,shedue线程完毕之后,执行notify唤醒扫描线程,继续向下执行,假设当前时间是12点,任务是2点半,这时需要把任务塞回队列,然后进行wait等待2个半小时,这时我们shedule新增的任务不就错过了.所以我们要确保在take和wait之间notify不能执行,所以给他俩进行加锁
在这里插入代码片
public MyTimer(){
Thread t1=new Thread(()->{
while (true) {
//取出队头元素
try {
synchronized (locker) {
Mytask mytask = queue.take();
//判断当前时间和任务设定时间大小关系
long curTime = System.currentTimeMillis();
if (curTime >= mytask.getTime()) {
//线程运行
mytask.getRunnable().run();
} else {
//把队头元素塞回去
queue.put(mytask);
//没到点就进行等待
locker.wait(mytask.getTime()-curTime);
}
}
} catch(InterruptedException e){
throw new RuntimeException(e);
}
}
});
t1.start();
}
//取任务
public void schedule(Runnable runnable,long after) throws InterruptedException {
Mytask mytask=new Mytask(runnable,after);
synchronized (locker){
queue.put(mytask);
locker.notify();
}
}
}
如果这样则会出现死锁情况
在这里插入代码片
public void schedule(Runnable runnable,long after) throws InterruptedException {
synchronized (locker){
Mytask mytask=new Mytask(runnable,after);
queue.put(mytask);
locker.notify();
}
}
假设先进行task取队首元素,此时队首就为空就触发阻塞进行等待,此时shedule要先进行加锁,才能给队首放元素,但是shedule获取不到锁,所以代码处于死锁状态
4.自己实现定时器的完整代码
在这里插入代码片
import java.util.PriorityQueue;
import java.util.Timer;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
//这个类表示一个任务
class Mytask implements Comparable<Mytask>{
//要执行的任务
private Runnable runnable;
//什么时间来执行
private long time;
@Override
public int compareTo(Mytask o) {
return (int)(this.time-o.time);
}
public Mytask(Runnable runnable, long delay) {
this.runnable = runnable;
//现在的时间
this.time = System.currentTimeMillis()+delay;
}
//因为一会要观察现在时间和设定闹钟时间大小关系,所以要得到这个时间
public Runnable getRunnable() {
return runnable;
}
public long getTime() {
return time;
}
}
class MyTimer{
private BlockingQueue<Mytask> queue=new PriorityBlockingQueue<>();
private Object locker=new Object();//定义一个锁对象
//创建一个扫描线程
//写了一个构造方法
public MyTimer(){
Thread t1=new Thread(()->{
while (true) {
//取出队头元素
try {
synchronized (locker) {
Mytask mytask = queue.take();
//判断当前时间和任务设定时间大小关系
long curTime = System.currentTimeMillis();
if (curTime >= mytask.getTime()) {
//线程运行
mytask.getRunnable().run();
} else {
//把队头元素塞回去
queue.put(mytask);
//没到点就进行等待
locker.wait(mytask.getTime()-curTime);
}
}
} catch(InterruptedException e){
throw new RuntimeException(e);
}
}
});
t1.start();
}
//放任务
public void schedule(Runnable runnable,long after) throws InterruptedException {
Mytask mytask=new Mytask(runnable,after);
queue.put(mytask);
synchronized (locker){
locker.notify();
}
}
}
public class Demo24 {
public static void main(String[] args) throws InterruptedException {
MyTimer myTimer=new MyTimer();
myTimer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("时间1");
}
},1000);
myTimer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("时间2");
}
},2000);
myTimer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("时间3");
}
},3000);
}
}