如果不出意外,这一篇应该是java多线程内容的倒数第三篇,后续应该会有一个再升级篇的一篇以及一篇番外篇(二),java多线程的内容就算是暂时的告一段落了。
这一篇的内容是关于java计时器的内容,内容不难,但是需要一点耐心以及细心,最重要的是多做尝试,自然就能理解其中的内容。
在java的类中,有一个主要负责计划任务的类就是Timer。所谓的计划任务,就是让任务在指定的时间执行。
Timer的主要作用是设置计划任务,但是封装任务的类却是TimerTask类。执行的任务需要放入TimerTask的子类中。
如图所示:
这样才能正确的设置和使用Timer。
下面我们来介绍几种方法:
这个方法的作用是在指定的日期执行一次某任务。
使用方法如下所示:
public class Test_thread{
private static Timer timer=new Timer();
static public class MyTask1 extends TimerTask{
@Override
public void run() {
System.out.println("MyTask1 运行了!时间为:"+new Date());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
static public class MyTask2 extends TimerTask{
@Override
public void run() {
System.out.println("MyTask2 运行了!时间为:"+new Date());
}
}
public static void main(String[] args) throws InterruptedException{
try {
MyTask1 task1=new MyTask1();
MyTask2 task2=new MyTask2();
SimpleDateFormat sdf1=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat sdf2=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString1="2018-10-12 11:55:00";
String dateString2="2018-10-12 11:56:00";
Date dateRef1=sdf1.parse(dateString1);
Date dateRef2=sdf2.parse(dateString2);
timer.schedule(task1, dateRef1);
timer.schedule(task2, dateRef2);
Thread.sleep(2000);
System.out.println("字符串1时间:"+dateRef1.toLocaleString()
+" 当前时间:"+new Date().toLocaleString());
System.out.println("字符串2时间:"+dateRef2.toLocaleString()
+" 当前时间:"+new Date().toLocaleString());
}catch(ParseException e) {
e.printStackTrace();
}
}
}
运行结果如下图所示:
在这个程序中,我们输入的时间均早于我运行程序时候的时间,在这种情况下就会立即执行task任务,同时,从结果中我们可以看出,虽然我们已经很早的就将task2的任务放入到schedual中去运行,但是却很晚才执行完毕。
之所以有这样的结果是因为TimerTask是以队列的方式顺序执行的,所以后面的任务很有可能因为前面的任务的执行时间过长,导致后面的任务时间也被延迟。如果用同一个计时器就会导致这样的结果,当然只要使用不同的计时器就要避免这种情况的发生。
大家可以尝试修改时间改在大家现在的时间的前和后,相信会很快了解计时器的用法,对这个有很大的收获。
该方法的作用是在指定的如期后,按照指定的时间间隔无限的执行某一任务。
字面上很好理解,但是实际操作过程中可能会有一些问题,例如下面的程序:
public class Test_thread{
private static Timer timer=new Timer();
private static Timer timer2=new Timer();
static public class MyTask1 extends TimerTask{
@Override
public void run() {
System.out.println("MyTask1 运行了!时间为:"+new Date());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} public static void main(String[] args) throws InterruptedException{
try {
MyTask1 task1=new MyTask1();
SimpleDateFormat sdf1=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString1="2018-10-12 11:55:00";
Date dateRef1=sdf1.parse(dateString1);
System.out.println("字符串1时间:"+dateRef1.toLocaleString()
+" 当前时间:"+new Date().toLocaleString());
timer.schedule(task1, dateRef1,4000);
}catch(ParseException e) {
e.printStackTrace();
}
}
}
执行结果如下所示:
可以看到原本设定的每四秒执行一次的结果,被对象中的沉睡打破,变为每五秒执行一次。如果把对象中的沉睡语句注释掉,就会看到又能够正常的执行了。
同样,当执行时间早于当前时间,就会立即执行。
作用是将自身从队列中清除。
public class Test_thread{
private static Timer timer=new Timer();
private static Timer timer2=new Timer();
static public class MyTask1 extends TimerTask{
@Override
public void run() {
System.out.println("MyTask1 运行了!时间为:"+new Date());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
static public class MyTask2 extends TimerTask{
@Override
public void run() {
System.out.println("MyTask2 运行了!时间为:"+new Date());
}
}
public static void main(String[] args) throws InterruptedException{
try {
MyTask1 task1=new MyTask1();
MyTask2 task2=new MyTask2();
SimpleDateFormat sdf1=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString1="2018-10-12 11:55:00";
Date dateRef1=sdf1.parse(dateString1);
timer.schedule(task1, dateRef1,2000);
timer.schedule(task2, dateRef1,2000);
System.out.println("字符串1时间:"+dateRef1.toLocaleString()
+" 当前时间:"+new Date().toLocaleString());
Thread.sleep(10000);
System.out.println("现在关闭task1!");
task1.cancel();
}catch(ParseException e) {
e.printStackTrace();
}
}
}
Timer中同样有cancel方法,但是与之不同的是,timer中的cancel方法是将所有的task清除掉,如下所示:
public class Test_thread{
private static Timer timer=new Timer();
private static Timer timer2=new Timer();
static public class MyTask1 extends TimerTask{
@Override
public void run() {
System.out.println("MyTask1 运行了!时间为:"+new Date());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
static public class MyTask2 extends TimerTask{
@Override
public void run() {
System.out.println("MyTask2 运行了!时间为:"+new Date());
}
}
public static void main(String[] args) throws InterruptedException{
try {
MyTask1 task1=new MyTask1();
MyTask2 task2=new MyTask2();
SimpleDateFormat sdf1=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString1="2018-10-12 11:55:00";
Date dateRef1=sdf1.parse(dateString1);
timer.schedule(task1, dateRef1,2000);
timer.schedule(task2, dateRef1,2000);
System.out.println("字符串1时间:"+dateRef1.toLocaleString()
+" 当前时间:"+new Date().toLocaleString());
Thread.sleep(10000);
System.out.println("现在关闭task1!");
timer.cancel();
}catch(ParseException e) {
e.printStackTrace();
}
}
}
在当前时间的基础上延后delay方法以后,执行一次TimerTask任务。
public class Test_thread{
private static Timer timer=new Timer();
private static Timer timer2=new Timer();
static public class MyTask1 extends TimerTask{
@Override
public void run() {
System.out.println(“MyTask1 运行了!时间为:”+new Date());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException{
MyTask1 task1=new MyTask1();
SimpleDateFormat sdf1=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
timer.schedule(task1, 2000);
Thread.sleep(10000);
}
在上一个方法的基础上,每隔一段时间执行一次任务:
public class Test_thread{
private static Timer timer=new Timer();
private static Timer timer2=new Timer();
static public class MyTask1 extends TimerTask{
@Override
public void run() {
System.out.println("MyTask1 运行了!时间为:"+new Date());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException{
MyTask1 task1=new MyTask1();
SimpleDateFormat sdf1=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
timer.schedule(task1, 2000,2000);
Thread.sleep(10000);
}
}
scheduleAtFixedRate是一种和schedule方法很类似的方法,但是,在非延时的情况下,scheduleAtFixedRate是参考上一次任务的“结束”时间来计算的。而不是schedule方法中的上一次任务的开始时间来计算的。
延时的情况两者是相同的,都是以结束时间来计算的。
如下所示:
public class Test_thread{
private static Timer timer=new Timer();
private static Timer timer2=new Timer();
static public class MyTask1 extends TimerTask{
@Override
public void run() {
System.out.println("MyTask1 运行了!时间为:"+new Date());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException{
try {
MyTask1 task1=new MyTask1();
SimpleDateFormat sdf1=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString1="2018-10-12 11:55:00";
Date dateRef1=sdf1.parse(dateString1);
System.out.println("现在启动Task1:"+new Date().toLocaleString());
timer.scheduleAtFixedRate(task1, dateRef1, 3000);
}catch(ParseException e) {
e.printStackTrace();
}
}
}
运行结果:
从结果可以看出,如果执行任务的时间没有被时延,则下一次执行任务的时间是上一次任务的开始时间加上delay时间。
scheduleAtFixedRate还有一个特性是追赶性。什么是追赶性?如果执行时间早于现在的时间,那么该方法会将从执行时间到现在时间之间的任务自动的执行。知道赶上当前时间即结束当前任务:
public class Test_thread{
private static Timer timer=new Timer();
private static Timer timer2=new Timer();
static public class MyTask1 extends TimerTask{
@Override
public void run() {
System.out.println("MyTask1 运行了!时间为:"+new Date());
}
}
public static void main(String[] args) throws InterruptedException{
try {
MyTask1 task1=new MyTask1();
SimpleDateFormat sdf1=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString1="2019-8-12 15:18:00";
Date dateRef1=sdf1.parse(dateString1);
System.out.println("字符串1时间:"+dateRef1.toLocaleString()
+" 当前时间:"+new Date().toLocaleString());
timer.scheduleAtFixedRate(task1, dateRef1, 5000);
Thread.sleep(10000);
}catch(ParseException e) {
e.printStackTrace();
}
}
}
结果如图所示:
请尝试这个程序的小伙伴,一定要调整好时间,大家就会明白所谓的追赶性究竟是什么东西了。当然schedule方法是没有追赶性的,它的执行是从当前时间开始的,但是不会补上以前没有执行的任务,这就是两者的区别。
以上就是java多线程计时器的内容,计时器的用途广泛,完全可以使用在很多的场合,但是有很多的细节需要大家注意。Java多线程倒数第三篇文章到此结束。