|
多线程案例
1. 定时器
定时器, 类似于一个 "闹钟"
. 代码中的定时器, 通常都是设定 “多长时间之后, 执行某个动作”.
例如: 客户端发送请求之后, 就需要等待服务器响应. 如果服务器一直不响应, 客户端也不能一直死等下去. 客户端经常会设置一个超时时间, 这时就可以使用定时器来实现.
标准库中的定时器
-
标准库中提供了一个
Timer
类. Timer 类的核心方法为schedule
. -
schedule
包含两个参数. 第一个参数指定即将要执行的任务代码, 第二个参数指定多长时间之后执行 (单位为毫秒).
public class Test {
public static void main(String[] args) {
//标准库的计时器
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("7:00了,该起床了");
}
},3000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("9:00了,该起床了");
}
},5000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("8:00了,该起床了");
}
},4000);
System.out.println("闹钟响了!!");
}
}
这时我们运行代码, 我们发现, 按照时间执行完上述任务之后, 进程并没有退出. Timer
内部需要一组线程来执行注册的任务, 而这里的线程是 前台线程, 会影响进程退出.
定时器的实现
schedule
的第一个参数是一个任务, 我们需要能够描述这个任务. (任务包含两方面信息, 需要执行的工作, 与他开始执行的时间)
class MyTask implements Comparable<MyTask>{
//要执行的任务
private Runnable runnable;
//什么时间来执行任务(一个时间戳)
private long time;
public MyTask(Runnable runnable, long dalay) {
this.runnable = runnable;
this.time = System.currentTimeMillis() + dalay;
}
public Runnable getRunnable() {
return runnable;
}
public long getTime() {
return time;
}
//实现 Comparable 接口, 便于下文 优先级队列的比较
@Override
public int compareTo(MyTask o) {
return (int)(this.time - o.time);
}
}
- 让
MyTimer
管理多个任务, 使用优先级阻塞队列 (保证每次取出等待的时间最短的任务, 与线程安全)
class MyTimer {
private BlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();
public void schedule(Runnable runnable, long after) throws InterruptedException {
MyTask myTask = new MyTask(runnable, after);
queue.put(MyTask);
}
}
- 从队列中去元素, 创建一个单独的扫描线程, 让这个线程
不停的
来检查队首元素, 时间是否到了, 如果到了则执行该任务
class MyTimer {