关于java多线程的概念以及基本用法:java多线程基础
5,定时器Timer
JDK中Timer类主要是负责计划任务的功能,也就是在指定的时间开始执行某一个任务,封装任务的类是TimerTask类,执行计划任务的代码要放进TimerTask的子类,因为它一个抽象类
5.1,方法schedule(TimerTask task,Date time)的测试
方法作用:在指定的日期执行一次某一任务
schedule(TimerTask task, long delay)
以当前时间为基准,延迟指定的毫秒后执行一次TimerTask任务。schedule(TimerTask task, Date time)
在指定的日期执行一次TimerTask任务。
如果日期time早于当前时间,则立刻执行
如果日期time晚于当前时间,等到time那个时间点再执行
1,执行任务的时间晚于当前时间:在未来执行的效果
public class hkjh {
private static Timer timer = new Timer();
static public class MyTask extends TimerTask
{
public void run()
{
System.out.println("运行了!时间为:" + new Date());
}
}
public static void main(String[] args) throws Exception
{
MyTask task = new MyTask();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = "2018-8-22 20:51:00";
Date dateRef = sdf.parse(dateString);
System.out.println("字符串时间:" + dateRef.toLocaleString() + " 当前时间:" + new Date().toLocaleString());
timer.schedule(task, dateRef);
}
}
字符串时间:2018-8-22 20:51:00 当前时间:2018-8-22 20:50:45
运行了!时间为:Wed Aug 22 20:51:00 CST 2018
等到了time再执行
2,计划时间早于当前时间:提前运行的效果
若 String dateString = “2018-8-22 19:51:00”;
结果为:
字符串时间:2018-8-22 19:51:00 当前时间:2018-8-22 20:52:18
运行了!时间为:Wed Aug 22 20:52:18 CST 2018
说明是立即执行
3,多个TimerTask任务及延时
public class Run2 {
private static Timer timer = new Timer();
static public class MyTask extends TimerTask
{
public void run()
{
System.out.println("运行了!时间为:" + new Date());
}
}
public static void main(String[] args) throws Exception
{
MyTask task1 = new MyTask();
MyTask task2 = new MyTask();
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString1 = "2018-8-23 9:51:00";
String dateString2 = "2018-8-23 9:51:02";
Date dateRef1 = sdf1.parse(dateString1);
Date dateRef2 = sdf2.parse(dateString2);
System.out.println("字符串时间:" + dateRef1.toLocaleString() + " 当前时间:" + new Date().toLocaleString());
System.out.println("字符串时间:" + dateRef2.toLocaleString() + " 当前时间:" + new Date().toLocaleString());
timer.schedule(task1, dateRef1);
timer.schedule(task2, dateRef2);
}
}
结果:
字符串时间:2018-8-23 9:51:00 当前时间:2018-8-23 9:50:40
字符串时间:2018-8-23 9:51:02 当前时间:2018-8-23 9:50:40
运行了!时间为:Thu Aug 23 09:51:00 CST 2018
运行了!时间为:Thu Aug 23 09:51:02 CST 2018
可以看到,运行时间和设置的时间一致,证明了未来可以执行多个任务。另外注意,Task是以队列的方式一个一个被顺序执行的,所以执行的时间有可能和预期的时间不一致,因为前面的任务可能消耗过长,后面任务的运行时间也有可能被延迟。
假如任务1计划10:00:00被执行,任务2计划10:00:10被执行,结果任务1执行了30秒,那么任务2将在10:00:30被执行,因为Task是被放入队列中的,因此必须一个一个顺序运行。
5.2,方法schedule(TimerTask task,Date firsttime,long period)的测试
该方法的作用是在指定的日期之后,按指定的间隔周期性地无限循环地执行某一任务
1,计划时间晚于当前时间:在未来执行的效果
public class Run {
static public class MyTask extends TimerTask{
public void run(){
System.out.println("运行了!时间为:" + new Date());
}
}
public static void main(String[] args) throws Exception{
try {
MyTask task = new MyTask();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = "2018-8-24 9:18:00";
Timer timer = new Timer();
Date dateRef = sdf.parse(dateString);
System.out.println("字符串时间:" + dateRef.toLocaleString() + " 当前时间:" + new Date().toLocaleString());
timer.schedule(task, dateRef, 4000);
}catch(ParseException e) {
e.printStackTrace();
}
}
}
结果:
字符串时间:2018-8-24 9:18:00 当前时间:2018-8-24 9:17:31
运行了!时间为:Fri Aug 24 09:18:00 CST 2018
运行了!时间为:Fri Aug 24 09:18:04 CST 2018
运行了!时间为:Fri Aug 24 09:18:08 CST 2018
运行了!时间为:Fri Aug 24 09:18:12 CST 2018
运行了!时间为:Fri Aug 24 09:18:16 CST 2018
运行了!时间为:Fri Aug 24 09:18:20 CST 2018
运行了!时间为:Fri Aug 24 09:18:24 CST 2018
。。。
。。。
计划时间2018-8-24 9:18:00晚于当前时间2018-8-24 9:17:31,在到了计划时间后,每隔4秒执行一次run()方法
2,计划时间早于当前时间:立即执行
如果计划时间早于当前时间,则立即执行
更改上面的代码
String dateString = "2018-8-24 8:18:00";
结果:
字符串时间:2018-8-24 8:18:00 当前时间:2018-8-24 9:25:55
运行了!时间为:Fri Aug 24 09:25:55 CST 2018
运行了!时间为:Fri Aug 24 09:25:59 CST 2018
运行了!时间为:Fri Aug 24 09:26:03 CST 2018
运行了!时间为:Fri Aug 24 09:26:07 CST 2018
运行了!时间为:Fri Aug 24 09:26:11 CST 2018
。。。
。。。
计划时间2018-8-24 8:18:00早于当前时间2018-8-24 9:25:55,所以立即执行run()方法,每隔4秒再次执行
3,任务执行时被延迟
上面的run方法都是假设执行时间小于long period的,如果run方法执行时间大于period则执行run()方法的间隔变成了run方法的执行时间
5.3,TimerTask类的cancel方法
TimerTask类中的cancel()方法的作用是将自身从任务队列中清除
public class Run2 {
static public class MyTaskA extends TimerTask{
public void run(){
System.out.println("A运行了!时间为:" + new Date());
this.cancel();
}
}
static public class MyTaskB extends TimerTask{
public void run(){
System.out.println("B运行了!时间为:" + new Date());
}
}
public static void main(String[] args) throws ParseException{
MyTaskA taskA = new MyTaskA();
MyTaskB taskB = new MyTaskB();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = "2018-8-24 9:10:00";
Timer timer = new Timer();
Date dateRef = sdf.parse(dateString);
System.out.println("字符串时间:" + dateRef.toLocaleString() + " 当前时间:" + new Date().toLocaleString());
timer.schedule(taskA, dateRef, 4000);
timer.schedule(taskB, dateRef, 4000);
}
}
结果:
字符串时间:2018-8-24 9:10:00 当前时间:2018-8-24 9:39:07
A运行了!时间为:Fri Aug 24 09:39:07 CST 2018
B运行了!时间为:Fri Aug 24 09:39:07 CST 2018
B运行了!时间为:Fri Aug 24 09:39:11 CST 2018
B运行了!时间为:Fri Aug 24 09:39:15 CST 2018
显然MyTaskA被清除了
5.4,Timer类的cancel方法
与上面的cancel()方法不同,Timer的cancel()可以将任务队列中的所有任务清空,但是如果cancel()方法没有争抢到queue锁,那么TimerTask类中的任务还是会继续执行
public class Run3 {
//static Timer timer = new Timer();
static int i=0;
static public class MyTask extends TimerTask{
@Override
public void run() {
System.out.println("正常执行了"+i);
//timer.cancel();
}
}
public static void main(String[] args) {
while(true) {
try {
i++;
Timer timer = new Timer();
MyTask task = new MyTask();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = "2018-8-24 9:10:00";
Date dateRef = sdf.parse(dateString);
timer.schedule(task, dateRef,1000);
timer.cancel();
} catch (ParseException e) {
e.printStackTrace();
}
}
}
}
结果:
正常执行了95
正常执行了142
正常执行了222
正常执行了254
正常执行了330
正常执行了389
正常执行了393
类似的还有其他的方法:
- 方法schedule(TimerTask task, long delay)以执行此方法的当前时间为参考时间,在此基础上延迟指定的毫秒后执行一次TimerTask任务
- 方法schedule(TimerTask task, long delay, long period),以执行此方法的当前时间为参考时间,在此时间基础上延迟指定毫秒数,再每隔指定时间无限循环执行run()方法
- 方法scheduleAtFixedRate(TimerTask task, Date firstTime, long period),在延时的场景下,schedule方法和scheduleAtFixedRate方法没有区别,它们的区别只是在非延时上。如果执行任务的时间没有被延时,对于schedule方法来说,下一次任务执行的时间参考的是上一次任务的开始时间来计算的;对于scheduleAtFixedRate方法来说,下一次任务执行的时间参考的是上一次任务的结束时间来计算的