介绍
正常情况下,定时器我们都是用Timer和TimerTask这两个类就能完成定时任务,并且设置延长时间和循环时间间隔。
ScheduledThreadPoolExecutor也能完成Timer一样的定时任务,并且时间间隔更加准确。
我在后台程序看看一下Timer执行程序是有可能延迟1、2毫秒,如果是1秒执行一次的任务,1分钟有可能延迟60毫秒,一小时延迟3600毫秒,相当于3秒,实际用户看不出什么区别。
但是,如果我的程序需要每40毫秒就执行一次任务,如果还是有1、2毫秒的误差,1秒钟就有25毫秒的误差,大概40秒就有1秒的误差,十几分钟就有十几秒的误差,这对UI显示来说是延迟非常严重的了。
而我用ScheduledThreadPoolExecutor来做40毫秒的间隔任务,一般十几分钟才有1秒多的误差,这个还是能接受的。
这也是我用ScheduledThreadPoolExecutor这个类的原因。
使用Timer和TimerTask存在一些缺陷
- Timer只创建了一个线程。当你的任务执行的时间超过设置的延时时间将会产生一些问题。
- Timer创建的线程没有处理异常,因此一旦抛出非受检异常,该线程会立即终止。
JDK 5.0以后推荐使用ScheduledThreadPoolExecutor。该类属于Executor Framework,它除了能处理异常外,还可以创建多个线程解决上面的问题
令牌桶的Java实现
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
class Schedule {
//增长速率
static int RATE = 4;
//桶最大值
static int TOTAL = 10;
//初始值
static int WATER = 0;
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(2);
public void run(){
executorService.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
WATER = Math.min(WATER + RATE,TOTAL);
}
},0,1000, TimeUnit.MILLISECONDS);
}
public static synchronized boolean tryAcquire(int num){
if (WATER >= num){
WATER -= num;
return true;
}
return false;
}
public static void main(String[] args) throws InterruptedException {
Schedule schedule = new Schedule();
schedule.run();
for (int i = 1; i < 20; i++){
if (tryAcquire(i)){
System.out.println("拿到了" + i);
}else{
System.out.println("拿不到" + i);
}
try {
Thread.sleep(500);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}