本文探讨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);
运行结果如下:
很明显,前面线程池中的线程在运行期间抛出异常并没有影响到后面的任务执行,这和Timer不一样。
以上就是ScheduledThreadPool基础以及ScheduledThreadPool和Timer的区别。