作者主页:paper jie_博客
本文作者:大家好,我是paper jie,感谢你阅读本文,欢迎一建三连哦。
本文于《JavaEE》专栏,本专栏是针对于大学生,编程小白精心打造的。笔者用重金(时间和精力)打造,将MySQL基础知识一网打尽,希望可以帮到读者们哦。
其他专栏:《MySQL》《C语言》《javaSE》《数据结构》等
内容分享:本期将会分享多线程案例 - 定时器
目录
什么是定时器
Java标准库中的定时器 - Timer
自定义一个定时器
定时器的组成
描述类MyTask
实现MyTask的比较
MyTimer类的构架
schedule方法
内置线程
线程安全与wait等待
具体代码
代码执行流程
定时器是我们程序猿来软件开发中一个很重要的组件.它的作用就是和闹钟一样. 当达到一个设定的时间后,就需要去执行某段指定的代码.定时器在我们实际开发中特别常见.比如一款游戏需要联网才能使用,要是在1秒之内没有数据返回给服务器,这时定时器就会发挥作用,断开与网络的连接然后尝试重连.
在我们Java标准库中就内置了一个Timer类,他就是定时器. 它里面有一个核心方法schedule就是用来注册任务的.
schedule里面有两个参数. 一个是时间到了需要执行的代码, 第二个是需要等待的时间,单位是毫秒.
public class ThreadDemo11 {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("hello 1000");
}
}, 1000);
}
}
这里用一个MyTimer类来表示定时器
1) 需要一个存放任务的优先级队列PriorityQueue
2) 需要一个MyTask类来描述任务
3) 这些任务是需要比较时间的,MyTask类需要实现Comparable接口
4) 需要定义一个内置线程来不断扫描任务观察时间到了没
5) 需要实现核心方法schedule来注册任务
Task这个类用来描述任务,里面包含一个Runnable对象和一个time时间戳. 需要执行的代码会通过传参的形式给到Runnable.
class MyTask3 {
//
private long time;
private Runnable runnable = null;
public MyTask3(Runnable runnable, long delay) {
this.runnable = runnable;
//需要执行的时刻
this.time = System.currentTimeMillis() + delay;
}
//获取时间
public long getTime() {
return time;
}
//获取需要执行的代码
public void run() {
runnable.run();
}
}
因为Mytask是描述任务,而这些任务需要放到优先级队列中比较,就需要实现Comparable或者比较器.这里我们实现Comparable接口.
class MyTask3 implements Comparable{
//
private long time;
private Runnable runnable = null;
public MyTask3(Runnable runnable, long delay) {
this.runnable = runnable;
//需要执行的时刻
this.time = System.currentTimeMillis() + delay;
}
//获取时间
public long getTime() {
return time;
}
//获取需要执行的代码
public void run() {
runnable.run();
}
//比较时间快慢方法
@Override
public int compareTo(MyTask3 o) {
return (int)(this.time - o.time);
}
}
这里最核心的就是priorityQueue这个优先级队列,用它来存放我们的任务. 所对象为我们后面起到一个加锁的作用.
class MyTimer3 {
//用优先级队列来存放任务
private PriorityQueue priorityQueue = new PriorityQueue<>();
//锁对象
private Object blocker = new Object();
public void schedule(Runnable runnable, long time) {
//核心方法
}
}
这里我们在核心方法schedule中创建出一个任务,再将这个任务放入优先级队列中.
public void schedule(Runnable runnable, long time) {
//创建一个任务
MyTask3 myTask3 = new MyTask3(runnable, time);
//将创建的任务放入优先级队列中
priorityQueue.offer(myTask3);
}
这里将内置线程放入构造方法中,当这个类一创建就开始执行. 这里通过while循环来不断扫描.
//内置线程
public MyTimer3() {
//创建一个线程
Thread t = new Thread(() -> {
//通过while来不断扫描
while(true) {
//判断优先级队列是不是空的
if(priorityQueue.isEmpty()) {
//为空就等待
//continue;
}
//当优先级队列中有任务时,取出任务
MyTask3 myTask3 = priorityQueue.peek();
//当前时间
long time = System.currentTimeMillis();
//如果到时间了就执行
if(time >= myTask3.getTime()) {
myTask3.run();
priorityQueue.take();
}else {
//时间没到等待
//continue;
}
}
});
t.start();
}
这里发现schedule方法和构造方法都会有对于priorityQueue优先级队列的读和修改,这里可能就会出现线程安全问题,我们就需要为他们加上锁. 为空时我们就通过wait方法等待,等调用schedule方法使用notify唤醒它. 当有元素时但时间没到也是使用wait有时间的等待,时间到了就解除等待.
public void schedule(Runnable runnable, long time) {
synchronized (blocker) {
//创建一个任务
MyTask3 myTask3 = new MyTask3(runnable, time);
//将创建的任务放入优先级队列中
priorityQueue.offer(myTask3);
//通过notify来唤醒扫描线程
blocker.notify();
}
}
//内置线程
public MyTimer3() {
//创建一个线程
Thread t = new Thread(() -> {
//通过while来不断扫描
while(true) {
//加锁
synchronized (blocker) {
try {
//判断优先级队列是不是空的
if(priorityQueue.isEmpty()) {
//为空就等待
blocker.wait();
}
//当优先级队列中有任务时,取出任务
MyTask3 myTask3 = priorityQueue.peek();
//当前时间
long time = System.currentTimeMillis();
//如果到时间了就执行
if(time >= myTask3.getTime()) {
myTask3.run();
priorityQueue.poll();
}else {
//时间没到等待 通过wait等待 有时间的等待.
blocker.wait(myTask3.getTime() - time);
}
}catch(InterruptedException o) {
o.printStackTrace();
}
}
}
});
//启动线程
t.start();
}
class MyTask3 implements Comparable{
//
private long time;
private Runnable runnable = null;
public MyTask3(Runnable runnable, long delay) {
this.runnable = runnable;
//需要执行的时刻
this.time = System.currentTimeMillis() + delay;
}
//获取时间
public long getTime() {
return time;
}
//获取需要执行的代码
public void run() {
runnable.run();
}
//比较时间快慢方法
@Override
public int compareTo(MyTask3 o) {
return (int)(this.time - o.time);
}
}
class MyTimer3 {
//用优先级队列来存放任务
private PriorityQueue priorityQueue = new PriorityQueue<>();
//所对象
private Object blocker = new Object();
//核心方法
public void schedule(Runnable runnable, long time) {
synchronized (blocker) {
//创建一个任务
MyTask3 myTask3 = new MyTask3(runnable, time);
//将创建的任务放入优先级队列中
priorityQueue.offer(myTask3);
//通过notify来唤醒扫描线程
blocker.notify();
}
}
//内置线程
public MyTimer3() {
//创建一个线程
Thread t = new Thread(() -> {
//通过while来不断扫描
while(true) {
//加锁
synchronized (blocker) {
try {
//判断优先级队列是不是空的
if(priorityQueue.isEmpty()) {
//为空就等待
blocker.wait();
}
//当优先级队列中有任务时,取出任务
MyTask3 myTask3 = priorityQueue.peek();
//当前时间
long time = System.currentTimeMillis();
//如果到时间了就执行
if(time >= myTask3.getTime()) {
myTask3.run();
priorityQueue.poll();
}else {
//时间没到等待 通过wait等待 有时间的等待.
blocker.wait(myTask3.getTime() - time);
}
}catch(InterruptedException o) {
o.printStackTrace();
}
}
}
});
//启动线程
t.start();
}
}