目录
1.1 schedule(TimeTask task,long delay)方法
1.2 schedule(TimeTask task,long delay,long period)方法
2. Java.util包下的TimerTask抽象类---指定需要执行的任务
二、Thread.sleep()和wait(long timeout)的区别
一、定时器
定时器可以间隔特定的时间,用来执行特定的程序
定时器执行任务时,不会占用我们当前的执行流
1. Java.util包下的Timer类---“闹钟”
1.1 schedule(TimeTask task,long delay)方法
等到delay毫秒后执行一次任务
import java.util.Timer;
import java.util.TimerTask;
/**
* @author happy
*/
public class UseTimer {
public static void main(String[] args) throws InterruptedException {
Timer timer = new Timer(); // 闹钟
TimerTask task = new TimerTask() { // 闹钟到时间后要做的任务
@Override
public void run() {
System.out.println("闹钟响了"); //输出“闹钟响了”
}
};
timer.schedule(task, 5000);
while (true) {} // 主线程死循环,所以之后的打印,一定不是主线程打印的
}
}
1.2 schedule(TimeTask task,long delay,long period)方法
周期性地执行任务,delay毫秒后,每过period毫秒执行一次任务
import java.util.Timer;
import java.util.TimerTask;
/**
* @author happy
*/
public class UseTimer {
public static void main(String[] args) throws InterruptedException {
Timer timer = new Timer(); // 闹钟
TimerTask task = new TimerTask() { // 闹钟到时间后要做的任务
@Override
public void run() {
System.out.println("闹钟响了");
}
};
// 1秒之后,每隔2秒打印一次“闹钟响了”
timer.scheduleAtFixedRate(task, 1000, 2000);
while (true) {
} // 主线程死循环,所以之后的打印,一定不是主线程打印的
}
}
2. Java.util包下的TimerTask抽象类---指定需要执行的任务
3.实现一个简易的定时器---只执行一次
写一个类似schedule(TimeTask task,long delay)方法
3.1MyTimer类
import java.util.concurrent.PriorityBlockingQueue;
/**
* @author happy
*/
public class MyTimer {
// 这里是普通属性,不是静态属性
private final PriorityBlockingQueue<MyTimerTask> queue = new PriorityBlockingQueue<>();
public MyTimer() {
Worker worker = new Worker();
worker.start();
}
// 不能使用静态内部类,否则看不到外部类的属性
class Worker extends Thread {
@Override
public void run() {
while (true) {
MyTimerTask task = null;
try {
task = queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
// task 应该有个应该执行的时刻(不能记录 delay)
long now = System.currentTimeMillis();
// now是现在的时间
// task.runAt是需要在什么时刻执行的时间
// delay是需要休眠多长时间
long delay = task.runAt - now;
if (delay <= 0) {
task.run();
} else {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
task.run();
}
}
}
}
public void schedule(MyTimerTask task, long delay) {
// 该方法非工作线程调用
task.runAt = System.currentTimeMillis() + delay;
queue.put(task);
}
}
3.2MyTimerTask类
/**
* @author happy
*/
public abstract class MyTimerTask implements Comparable<MyTimerTask> {
// 优先级队列,要求元素具备比较能力
// runAt越小说明应该越先执行
long runAt; // 这个任务应该在何时运行(记录为 ms 为单位的时间戳)
abstract public void run();
@Override
public int compareTo(MyTimerTask o) {
if (runAt < o.runAt) {
return -1;
} else if (runAt > o.runAt) {
return 1;
} else {
return 0;
}
}
}
3.3测试1
public class Main {
public static void main(String[] args) throws InterruptedException {
MyTimerTask t1 = new MyTimerTask() {
@Override
public void run() {
System.out.println("5s 之后");
}
};
MyTimerTask t2 = new MyTimerTask() {
@Override
public void run() {
System.out.println("4s 之后");
}
};
MyTimerTask t3 = new MyTimerTask() {
@Override
public void run() {
System.out.println("3s 之后");
}
};
MyTimerTask t4 = new MyTimerTask() {
@Override
public void run() {
System.out.println("2s 之后");
}
};
MyTimerTask t5 = new MyTimerTask() {
@Override
public void run() {
System.out.println("1s 之后");
}
};
MyTimer myTimer = new MyTimer();
myTimer.schedule(t1, 5000);
myTimer.schedule(t2, 4000);
myTimer.schedule(t3, 3000);
myTimer.schedule(t4, 2000);
myTimer.schedule(t5, 1000);
}
}
输出结果
3.4测试2
public class Main {
public static void main(String[] args) throws InterruptedException {
MyTimerTask t1 = new MyTimerTask() {
@Override
public void run() {
System.out.println("5s 之后");
}
};
MyTimerTask t2 = new MyTimerTask() {
@Override
public void run() {
System.out.println("1.5s 之后");
}
};
MyTimer myTimer = new MyTimer();
myTimer.schedule(t1, 5000);
TimeUnit.SECONDS.sleep(1);
myTimer.schedule(t2, 1500);
}
}
输出结果
此时出错的原因是t1会休眠5s,在t1休眠期间t1是不知道t2只要休眠1.5s,导致t1执行完之后才会发现t2
3.5改正测试2
不用Thread.sleep(),改用带超时的wait()
import java.util.concurrent.PriorityBlockingQueue;
/**
* @author happy
*/
public class MyTimer {
// 这里是普通属性,不是静态属性
private final PriorityBlockingQueue<MyTimerTask> queue = new PriorityBlockingQueue<>();
private final Object newTaskComing = new Object();
public MyTimer() {
Worker worker = new Worker();
worker.start();
}
// 不能使用静态内部类,否则看不到外部类的属性
class Worker extends Thread {
@Override
public void run() {
while (true) {
MyTimerTask task = null;
try {
task = queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
// task 应该有个应该执行的时刻(不能记录 delay)
long now = System.currentTimeMillis();
// now是现在的时间
// task.runAt是需要在什么时刻执行的时间
// delay是需要休眠多长时间
long delay = task.runAt - now;
if (delay <= 0) {
task.run();
}else {
try {
// 应该在两种条件下醒来:
// 1. 有新的任务过来了(任务可能比当前最小的任务更靠前)
// 2. 没有新任务来,但到了该执行该任务的时候了
synchronized (newTaskComing) {
newTaskComing.wait(delay);
}
// 如果发现新任务需要执行的时间在当前时间之前
// 说明需要先执行新任务
// 如果不是,就先把新任务放回到队列中,重新取最小的任务
if (System.currentTimeMillis() >= task.runAt) {
task.run();
} else {
queue.put(task);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public void schedule(MyTimerTask task, long delay) {
// 该方法非工作线程调用
task.runAt = System.currentTimeMillis() + delay;
queue.put(task);
synchronized (newTaskComing) {
newTaskComing.notify();
}
}
}
运行结果
二、Thread.sleep()和wait(long timeout)的区别
1.语义不同;一个是休眠,一个是等待
2. sleep()是有固定的休眠时间的,wait(timeout)可能 <= 休眠时间;wait(timeout)有两个结束条件:超时时间
3.sleep()是 Thread 的静态方法wait(timeout) 是 Object 的方法
4.wait 需要搭配 synchronized 使用;sleep 不需要
三、线程池
- 标准库的线程池---ThreadPoolExecutor
- 因为创建/销毁一个线程都是有成本的
- 线程池是按照需求提前创建很多个线程,一旦有一个新的任务,就交给储备的线程来处理有四个构造函数
对于第四个构造方法,假设我们把线程池看成一个公司
- 刚开始的时候,线程池里没有工作线程
- 当线程数量 < maximumPoolSize,之间创建一个新的正式员工
- 当线程数量 = maximumPoolSize,暂时把任务放到队列
- 当线程到达上限而且队列也满了的时候,就会执行拒绝策略