定时器:
定时器就相当于闹钟,时间一到就执行相关的任务。
1.标准库中提供了一个Timer类,Timer类的核心方法是为schedule。
2.在schedule方法中有两个参数,第一个参数指定即将要执行的任务代码,第二个参数指定多长时间之后执行。
正常描述任务用Runnable,这里用TimerTask包装了一下
核心还是重写run方法。
import java.util.Timer;
import java.util.TimerTask;
public class Test{
public static void main(String[] args) {
Timer timer=new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("3s后执行任务");
}
},3000);
}
}
定时器的实现:
1.首先创建一个类MyTimerTask,用来表示任务。
2.定时器中,使用阻塞队列将多个任务管理起来(确保时间最早的要最先执行)优先级队列是优选的
3.定时器中的schedule方法,目的把任务添加到队列中。
4.在定时器中额外创建一个线程,负责执行队列中的任务。(和线程池不同,线程池只要阻塞队列不为空就读取任务并继续执行,此处还要检查任务的执行时间是否到了,到了时间才执行)
在将任务添加到队列中时,必须要明确比较规则(让时间最早的任务在队首,而时间最晚的任务在队尾)
注意:使用该方法,要实现Comparable接口。这个方法是交给阻塞队列去调用的。(只需写好规则即可)
import java.util.PriorityQueue;
class MyTimerTask implements Comparable<MyTimerTask>{
private Runnable runnable;//需要执行的任务
private long time;//执行任务的时间
public MyTimerTask(Runnable runnable, long time){
this.runnable=runnable;
this.time=time;
}
@Override
public int compareTo(MyTimerTask o) {//这是提供给阻塞队列的方法,用来排列执行任务的先后顺序
return (int) (this.time-o.time);
}
public long getTime() {
return time;
}
public void run(){
runnable.run();
}
}
class MyTimer{
private PriorityQueue<MyTimerTask> queue=new PriorityQueue<>();
public void schedule(Runnable task,int time){
synchronized (this){
MyTimerTask tk=new MyTimerTask(task,System.currentTimeMillis()+time);
queue.offer(tk);
this.notify();
}
}
public MyTimer(){//构造方法本身也可以加synchronized,但是需要保护的是线程里面的方法
Thread t=new Thread(()->{
while(true){
synchronized (this){
while(queue.isEmpty()){//如果阻塞队列为空,就一直等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
MyTimerTask task=queue.peek();
if(task.getTime()>System.currentTimeMillis()){//执行任务的时间和系统时间比较
try {
this.wait(task.getTime()-System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{//说明到了执行任务的时间
task.run();
queue.poll();
}
}
}
});
t.start();
}
}
public class Test{
public static void main(String[] args) {
MyTimer time=new MyTimer();
time.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello 3000");
}
},3000);
time.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello 2000");
}
},2000);
time.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello 1000");
}
},1000);
}
}
为什么在schedule中要加synchronized:因为在schedule方法是一个线程,但是定时器内部构造方法还有一个线程,当两个线程同时操作队列时,就要涉及到线程安全问题。
带有定时器的线程池:
要创建一个带有定时器的线程池,可以使用Java中ScheduledThreadPoolExecutor
的类。下面是一个示例代码:
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Test2 {
public static void main(String[] args) {
ScheduledExecutorService stpe= Executors.newScheduledThreadPool(3);
stpe.schedule(new Runnable() {
@Override
public void run() {
System.out.println("我第一个执行 1000");
}
},1, TimeUnit.SECONDS);
stpe.schedule(new Runnable() {
@Override
public void run() {
System.out.println("我第二个执行 2000");
}
},2, TimeUnit.SECONDS);
stpe.shutdown();//关闭线程池
}
}
在上面的代码中,我们使用Executors.newScheduledThreadPool
方法创建了一个带有定时器的线程池,指定了最大线程数为3。然后,我们使用executor.schedule
方法提交了两个任务,并指定了延迟执行的时间和时间单位。这两个任务将在指定的时间后被执行。
最后,我们调用executor.shutdown
方法关闭线程池。