本文探讨Java线程池中的ScheduledThreadPool
ScheduledThreadPool可以用来处理延时任务或者定时任务。
过时的处理延时任务或者定时任务的类:Timer
我们关于定时/周期操作都是通过Timer来实现的。Timer简单易用,但是Timer存在一些危险:
1、Timer是基于绝对时间的,容易受到系统时钟的影响。
2、Timer只新建一个线程来执行所有的TimerTask,所以前面的任务会影响到后面的任务。
3、Timer不会捕获TimerTask的异常,只是简单地停止。因此会影响到其他TimeTask的执行。
对于Timer的一个对象timer,
timer.schedule(task, delay, period);可以延时delay后执行实现继承了TimerTask的任务,之后每过period再执行以此task;
timer.schedule(task, delay);可以延时delay后执行task。
我们来测试一下Timer,让我们看看它的缺点:
我们不测试它与系统时钟的关系了,先测试前面的任务会影响后面的任务的问题,
首先建立一个MyTimerTask类来定义Timer任务:
package newScheduledThreadPool;
import java.util.TimerTask;
public class MyTimerTask extends TimerTask {
private int i ;
private long time;//记录运行时间
public MyTimerTask(int i,long time){
this.i = i;
this.time = time;
}
@Override
public void run() {
i++;
System.out.println(Thread.currentThread().getName()+" "+i+"执行时间为"+(System.currentTimeMillis()-time));
try {
Thread.sleep(1000);//线程暂停一会儿
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
然后再Test类中做测试:
package newScheduledThreadPool;
import java.util.Timer;
import java.util.TimerTask;
public class Test {
public static void main(String[] args) {
Timer timer = new Timer();
int i = 0;
long time = System.currentTimeMillis();
timer.schedule(new MyTimerTask(i,time){}, 1000);
timer.schedule(new MyTimerTask(i,time){}, 1000);//观察前一个timer任务时间对这个任务时间的影响
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
timer.cancel();//关闭timer
}
}
运行结果如下:
很明显后面的任务的执行时间受到了前面任务的影响,这是Timer的一个缺点。
另外来测试抛出异常Timer就简单停止的问题:
还是首先写一个MyTimeTask类定义Timer任务:
package newScheduledThreadPool;
import java.util.TimerTask;
public class MyTimerTask extends TimerTask {
private int i ;
public MyTimerTask(int i){
this.i = i;
}
@Override
public void run() {
i++;
if(i==2)
throw new RuntimeException("i为2");
System.out.println(Thread.currentThread().getName()+" "+i));
}
}
然后使用下面的代码启动Timer进行测试(我就不写包名,类名了):
Timer timer = new Timer();
int i = 0;
int j = 0;
// long time = System.currentTimeMillis();
timer.schedule(new MyTimerTask(i){}, 1000,1000);//i加到2会抛出异常
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
timer.schedule(new MyTimerTask(j){}, 1000);
运行结果如下:
很明显当任务抛出异常,Timer就cancel了。
下面介绍ScheduledThreadPool:
ScheduledThreadPool内部使用的阻塞队列是DelayQueue,这是一个无界、带延迟的阻塞队列,只有当延迟时间过了才能从这个阻塞队列中取出当中的元素。
对于newScheduledExecutorService()这个Executors的静态方法返回的SchedluedExecutorService类型对象 scheduledThreadPool,
scheduledThreadPool.schedule(command, delay, unit)表示过了delay的unit之后执行command
scheduledThreadPool.scheduleWithFixedDelay(command, initialDelay, delay, unit)表示首先过了initialDelay之后执行command,然后每过delay的unit之后再执行command。
下面用ScheduledThreadPool来解决Timer的缺点。
首先写实现Runnable接口的ThreadPoolTask类表示ScheduledThreadPool的执行任务:
package newScheduledThreadPool;
public class ThreadPoolTask implements Runnable {
private long time;
public ThreadPoolTask(long time) {
this.time = time;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行"+" "+"用时"+(System.currentTimeMillis()-time));
try {
Thread.sleep(1000);//让线程休息一会儿
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
然后用下面的代码进行测试(我就不写类名包名了):
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool();
long time = System.currentTimeMillis();
scheduledThreadPool.schedule(new ThreadPoolTask(time),1000,TimeUnit.MILLISECONDS);
scheduledThreadPool.schedule(new ThreadPoolTask(time),1000,TimeUnit.MILLISECONDS);//观察上个任务对这个任务的影响
try {
TimeUnit.MILLISECONDS.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
scheduledThreadPool.shutdown();//关闭线程池
运行结果如下:
很明显前面的任务的执行时间没有影响到后面的任务。
另外测试scheduledThreadPool中线程在运行时抛出异常后面的任务还能不能进行:
首先改一下ThreadPoolTask类:
package newScheduledThreadPool;
public class ThreadPoolTask implements Runnable {
private int i;
public ThreadPoolTask(int i) {
this.i = i;
}
@Override
public void run() {
if(i==2)
throw new IllegalStateException("i=2");//当i=2的时候抛出异常 观察对后面的线程池中线程的任务执行的影响
System.out.println(Thread.currentThread().getName()+"执行"+" "+i++);
}
}
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);//相当于定时的FixedThreadPool
scheduledThreadPool.scheduleWithFixedDelay(new ThreadPoolTask(i), 1000,1000, TimeUnit.MILLISECONDS);
try {
System.out.println("开始暂停");
Thread.sleep(3000);//在主线程sleep期间 scheduledThreadPool中的线程会抛出异常 观察对下面线程池执行定时任务的影响
System.out.println("暂停结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
scheduledThreadPool.scheduleWithFixedDelay(new ThreadPoolTask(j), 1000, 1000, TimeUnit.MILLISECONDS);
运行结果如下:
以上就是ScheduledThreadPool基础以及ScheduledThreadPool和Timer的区别。