赞
踩
Java 中的计划任务 Timer 工具类提供了以计时器或计划任务的功能来实现按指定时间或时间间隔执行任务,但由于 Timer 工具类并不是以池 pool ,而是以队列的方式来管理线程的,所以在高并发的情况下运行效率较低,在新版 JDK 中提供了 SchedleExecutorService对象来解决效率与定时任务的功能。
----> interface ScheduledExecutorService extends ExecutorService
----> interface ExecutorService extends Executor
----->class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService
1.schedule()方法是延迟运行方法,延时delay时间后执行任务,并不是周期性执行方法。
2.schedule()方法不会造成堵塞,该方法启动任务之后,代码会继续向下执行。
3.由于返回值是ScheduledFuture,执行该接口的get()方法时,会堵塞后面代码的执行。
//返回值ScheduledFuture继承Future //参数Runnable,没返回值 public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) { if (command == null || unit == null) throw new NullPointerException(); RunnableScheduledFuture<?> t = decorateTask(command, new ScheduledFutureTask<Void>(command, null, triggerTime(delay, unit))); delayedExecute(t); return t; } //返回值ScheduledFuture继承Future //参数Callable,有返回值 public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) { if (callable == null || unit == null) throw new NullPointerException(); RunnableScheduledFuture<V> t = decorateTask(callable, new ScheduledFutureTask<V>(callable, triggerTime(delay, unit))); delayedExecute(t); return t; }
public class ScheduledExecutorServiceDemo { //任务线程 public static class MyCallable implements Callable{ String name; long sleepTime; public MyCallable(String name, long sleepTime) { super(); this.name = name; this.sleepTime = sleepTime; } @Override public Object call() throws Exception { System.out.println(name+"开始运行,时间:"+System.currentTimeMillis()); //休眠时间 Thread.sleep(sleepTime); System.out.println(name+"结束运行,时间:"+System.currentTimeMillis()); return name+"结束运行!"; } } public static void main(String[] args) { ScheduledExecutorService scheduledes = null; try { //创建单线程任务池 scheduledes = new ScheduledThreadPoolExecutor(1); System.out.println("开始执行定时任务!"+System.currentTimeMillis()); //开始执行第一个任务,等待时间已经开始计时 ScheduledFuture FutureA = scheduledes.schedule(new MyCallable("任务1",4000),2, TimeUnit.SECONDS); //System.out.println(FutureA.get()); //开始执行第二个任务,等待时间已经开始计时 ScheduledFuture FutureB = scheduledes.schedule(new MyCallable("任务2",0),2, TimeUnit.SECONDS); //System.out.println(FutureB.get()); System.out.println("结束执行定时任务!"+System.currentTimeMillis()); } catch (Exception e) { e.printStackTrace(); } } } 开始执行定时任务!1596525151924 结束执行定时任务!1596525151927 任务1开始运行,时间:1596525153927 任务1结束运行,时间:1596525157927 任务2开始运行,时间:1596525157927 任务2结束运行,时间:1596525157927
上面实验中,FutureA.get()方法加了注释,所以不堵塞System.out.println(“结束执行定时任务!”+System.currentTimeMillis());代码的执行。
其次new ScheduledThreadPoolExecutor(1),参数为1表示任务池中只有一个线程,但是此时我放入2个任务,也就是会排队执行,但是定时器不会排队,当schedule()方法执行完定时器就启动了,任务1开始运行,时间:1596525153927,执行4s,在时间为1596525157927时结束,4s时间内任务2定时器走完了(2s定时器),所以任务1结束可以立即执行任务2。
用法同Callable,就是没有返回值。
1.该方法不堵塞,且该方法参数要求Runnable ,不可用Callable,因此无返回值。
2.initialDelay时间后开始执行任务,且每隔period时间执行一次,间隔时间从任务开始执行开始计算。
3.如果任务的执行业务时间超过了period间隔时间,则一次任务结束后立即开始下一次任务。
//参数Runnable ,无返回值 public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { if (command == null || unit == null) throw new NullPointerException(); if (period <= 0) throw new IllegalArgumentException(); ScheduledFutureTask<Void> sft = new ScheduledFutureTask<Void>(command, null, triggerTime(initialDelay, unit), unit.toNanos(period)); RunnableScheduledFuture<Void> t = decorateTask(command, sft); sft.outerTask = t; delayedExecute(t); return t; }
public class ScheduledExecutorServiceDemo { //任务线程 public static class MyRunnable implements Runnable{ String name; long sleepTime; public MyRunnable(String name, long sleepTime) { super(); this.name = name; this.sleepTime = sleepTime; } @Override public void run() { try { System.out.println(name+"开始运行,时间:"+System.currentTimeMillis()); //休眠时间 Thread.sleep(sleepTime); System.out.println(name+"结束运行,时间:"+System.currentTimeMillis()); } catch (Exception e) { e.printStackTrace(); } } } public static void main(String[] args) { ScheduledExecutorService scheduledes = null; try { //创建单线程任务池 scheduledes = Executors.newSingleThreadScheduledExecutor(); System.out.println("开始执行定时任务!"+System.currentTimeMillis()); //开始执行第一个任务,3s执行一次,每次执行业务时间4s ScheduledFuture FutureA = scheduledes.scheduleAtFixedRate(new MyRunnable("任务1",4000),2,3, TimeUnit.SECONDS); System.out.println("结束执行定时任务!"+System.currentTimeMillis()); } catch (Exception e) { e.printStackTrace(); } } } 开始执行定时任务!1596527222370 //立即执行不堵塞 结束执行定时任务!1596527222376 //2s之后开始执行第一次任务 任务1开始运行,时间:1596527224373 //任务执行业务时间4s,定时器计时从开始执行业务开始,当一次任务结束,立即执行下一次任务 任务1结束运行,时间:1596527228373 //从上次任务开始到现在过了3s,立即开始下一次任务 任务1开始运行,时间:1596527228373 任务1结束运行,时间:1596527232373 任务1开始运行,时间:1596527232373 任务1结束运行,时间:1596527236381 任务1开始运行,时间:1596527236381 任务1结束运行,时间:1596527240401 任务1开始运行,时间:1596527240401 任务1结束运行,时间:1596527244401
1.该方法不堵塞,且该方法参数要求Runnable ,不可用Callable,因此无返回值。
2.initialDelay时间后开始执行任务,且每隔period时间执行一次,间隔时间从上次任务结束开始计算。
3.如果任务的执行业务时间超过了period间隔时间,无影响,在任务结束后间隔period时间执行下次任务。
//参数要求Runnable ,无返回值 public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { if (command == null || unit == null) throw new NullPointerException(); if (delay <= 0) throw new IllegalArgumentException(); ScheduledFutureTask<Void> sft = new ScheduledFutureTask<Void>(command, null, triggerTime(initialDelay, unit), unit.toNanos(-delay)); RunnableScheduledFuture<Void> t = decorateTask(command, sft); sft.outerTask = t; delayedExecute(t); return t; }
public class ScheduledExecutorServiceDemo { //任务线程 public static class MyRunnable implements Runnable{ String name; long sleepTime; public MyRunnable(String name, long sleepTime) { super(); this.name = name; this.sleepTime = sleepTime; } @Override public void run() { try { System.out.println(name+"开始运行,时间:"+System.currentTimeMillis()); //休眠时间 Thread.sleep(sleepTime); System.out.println(name+"结束运行,时间:"+System.currentTimeMillis()); } catch (Exception e) { e.printStackTrace(); } } } public static void main(String[] args) { ScheduledExecutorService scheduledes = null; try { //创建单线程任务池 scheduledes = Executors.newSingleThreadScheduledExecutor(); System.out.println("开始执行定时任务!"+System.currentTimeMillis()); //开始执行第一个任务,3s执行一次,每次执行业务时间4s ScheduledFuture FutureA = scheduledes.scheduleWithFixedDelay(new MyRunnable("任务1",4000),2,3, TimeUnit.SECONDS); System.out.println("结束执行定时任务!"+System.currentTimeMillis()); } catch (Exception e) { e.printStackTrace(); } } } 开始执行定时任务!1596527815175 结束执行定时任务!1596527815178 任务1开始运行,时间:1596527817178 任务1结束运行,时间:1596527821200 //不管任务执行时间需要多久,在上次任务结束后,间隔period时间再执行下一次任务 任务1开始运行,时间:1596527824200 任务1结束运行,时间:1596527828200 任务1开始运行,时间:1596527831201 任务1结束运行,时间:1596527835201
方法 getQueue()的作用是取得队列中的任务,而这些任务是未来将要运行 ,正在运行的任务不在此队列中使用scheduleAtFixedRate()和 scheduleWithFixedDelay() 两个方法实现周期性执行任务时,未来欲执行的任务都放入此队列中。
remove()删除某个任务。
示例
public class ScheduledExecutorServiceDemo { //任务线程 public static class MyRunnable implements Runnable{ String name; long sleepTime; public MyRunnable(String name, long sleepTime) { super(); this.name = name; this.sleepTime = sleepTime; } @Override public void run() { try { System.out.println(name+"开始运行,时间:"+System.currentTimeMillis()); //休眠时间 Thread.sleep(sleepTime); System.out.println(name+"结束运行,时间:"+System.currentTimeMillis()); } catch (Exception e) { e.printStackTrace(); } } } public static void main(String[] args) { ScheduledThreadPoolExecutor scheduledes = null; try { //创建线程任务池 scheduledes = new ScheduledThreadPoolExecutor(5); System.out.println("开始执行定时任务!"+System.currentTimeMillis()); //开始执行任务 ScheduledFuture FutureA = scheduledes.scheduleWithFixedDelay( new MyRunnable("任务1",1000),2,3, TimeUnit.SECONDS); ScheduledFuture FutureB = scheduledes.scheduleWithFixedDelay( new MyRunnable("任务2",1000),2,3, TimeUnit.SECONDS); ScheduledFuture FutureC = scheduledes.scheduleWithFixedDelay( new MyRunnable("任务3",1000),2,3, TimeUnit.SECONDS); ScheduledFuture FutureD = scheduledes.scheduleWithFixedDelay( new MyRunnable("任务4",1000),2,3, TimeUnit.SECONDS); //等待3s Thread.sleep(4000); //获取任务队列 BlockingQueue<Runnable> que = scheduledes.getQueue(); Iterator<Runnable> iterator = que.iterator(); while(iterator.hasNext()){ System.out.println("队列中的任务:" + (Runnable)iterator.next()); } System.out.println("删除任务1:" + scheduledes.remove((Runnable)FutureA)); System.out.println("结束执行定时任务!"+System.currentTimeMillis()); } catch (Exception e) { e.printStackTrace(); } } } 开始执行定时任务!1596534633303 任务1开始运行,时间:1596534635306 任务2开始运行,时间:1596534635306 任务3开始运行,时间:1596534635307 任务4开始运行,时间:1596534635307 任务4结束运行,时间:1596534636309 任务1结束运行,时间:1596534636315 任务2结束运行,时间:1596534636343 任务3结束运行,时间:1596534636367 队列中的任务:java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@5451c3a8 队列中的任务:java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@2626b418 队列中的任务:java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@5a07e868 队列中的任务:java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@76ed5528 删除任务1:true 结束执行定时任务!1596534637307 任务4开始运行,时间:1596534639310 任务2开始运行,时间:1596534639347 任务3开始运行,时间:1596534639368 任务4结束运行,时间:1596534640310 任务2结束运行,时间:1596534640347 任务3结束运行,时间:1596534640368 任务4开始运行,时间:1596534643310 任务2开始运行,时间:1596534643347 任务3开始运行,时间:1596534643368 任务4结束运行,时间:1596534644310 任务2结束运行,时间:1596534644347 任务3结束运行,时间:1596534644368 任务4开始运行,时间:1596534647311 任务2开始运行,时间:1596534647348 任务3开始运行,时间:1596534647369 任务4结束运行,时间:1596534648311 任务2结束运行,时间:1596534648348 任务3结束运行,时间:1596534648369 任务4开始运行,时间:1596534651311 任务2开始运行,时间:1596534651348 任务3开始运行,时间:1596534651369
1.cancel(boolean)属于future的方法,表示取消任务。参数boolean
1.如果参数传的是false,cancel(false)只能取消还没有开始的任务,若任务已经开始了,就任由其运行下去。如果是定时任务,当前开始的这次任务不会结束,这次任务结束后,后续任务全部取消,但是任务依然在队列里。
2.如果参数传的是true,cancel(true),任务立即取消,正在运行的也会中断,但是其中断还是更改的中断状态(需要配合线程的isInterrupted使用)。
3.Future.cancel(true)适用于:长时间处于运行的任务,并且能够处理interruption
4.Future.cancel(false)适用于:没有处理或者不清楚有没有处理interruption的任务,强行cancel(true)会抛异常或者是 需要等待已经开始的任务执行完成再结束的任务。
2.setRemoveOnCancelPolicy(boolean)方法,作用是是否将取消的任务从队列中删除。配合上面方法。
public class ScheduledExecutorServiceDemo { //任务线程 public static class MyRunnable implements Runnable{ String name; long sleepTime; public MyRunnable(String name, long sleepTime) { super(); this.name = name; this.sleepTime = sleepTime; } @Override public void run() { try { System.out.println(name+"开始运行,时间:"+System.currentTimeMillis()); //休眠时间 Thread.sleep(sleepTime); System.out.println(name+"结束运行,时间:"+System.currentTimeMillis()); } catch (Exception e) { e.printStackTrace(); } } } public static void main(String[] args) { ScheduledThreadPoolExecutor scheduledes = null; try { //创建线程任务池 scheduledes = new ScheduledThreadPoolExecutor(5); System.out.println("开始执行定时任务!"+System.currentTimeMillis()); //开始执行任务 ScheduledFuture FutureA = scheduledes.scheduleWithFixedDelay( new MyRunnable("任务1",2000),2,1, TimeUnit.SECONDS); ScheduledFuture FutureB = scheduledes.scheduleWithFixedDelay( new MyRunnable("任务2",1000),2,1, TimeUnit.SECONDS); ScheduledFuture FutureC = scheduledes.scheduleWithFixedDelay( new MyRunnable("任务3",1000),2,1, TimeUnit.SECONDS); ScheduledFuture FutureD = scheduledes.scheduleWithFixedDelay( new MyRunnable("任务4",1000),2,1, TimeUnit.SECONDS); //等待3s Thread.sleep(3000); //队列中清空取消的任务 scheduledes.setRemoveOnCancelPolicy(true); //取消FutureA任务,正在运行的不取消 FutureA.cancel(false); //获取队列长度 BlockingQueue que = scheduledes.getQueue(); Iterator iterator = que.iterator(); System.out.println("现在队列长度:" + que.size()); while (iterator.hasNext()){ System.out.println("队列中任务:" + iterator.next()); } System.out.println("结束执行定时任务!"+System.currentTimeMillis()); } catch (Exception e) { e.printStackTrace(); } } } 开始执行定时任务!1596597074128 任务1开始运行,时间:1596597076131 任务2开始运行,时间:1596597076135 任务3开始运行,时间:1596597076136 任务4开始运行,时间:1596597076136 任务2结束运行,时间:1596597077137 任务4结束运行,时间:1596597077137 任务3结束运行,时间:1596597077138 现在队列长度:3 队列中任务:java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@5451c3a8 队列中任务:java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@2626b418 队列中任务:java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@5a07e868 结束执行定时任务!1596597077140 任务1结束运行,时间:1596597078131 任务2开始运行,时间:1596597078138 任务4开始运行,时间:1596597078138 任务3开始运行,时间:1596597078139 任务4结束运行,时间:1596597079138 任务2结束运行,时间:1596597079138 任务3结束运行,时间:1596597079139 任务4开始运行,时间:1596597080139 任务3开始运行,时间:1596597080139 任务2开始运行,时间:1596597080139 任务4结束运行,时间:1596597081139 任务2结束运行,时间:1596597081139 任务3结束运行,时间:1596597081139 任务2开始运行,时间:1596597082139 任务3开始运行,时间:1596597082139 任务4开始运行,时间:1596597082140 任务2结束运行,时间:1596597083139 任务3结束运行,时间:1596597083139 任务4结束运行,时间:1596597083140
上面实验可知,cancel(false),没有取消正在运行的任务1,等任务1本次结束之后再取消的。setRemoveOnCancelPolicy(true);方法将取消的任务从队列中进行了删除,所以队列任务数变为3。
public class ScheduledExecutorServiceDemo { //任务线程 public static class MyRunnable implements Runnable{ String name; long sleepTime; public MyRunnable(String name, long sleepTime) { super(); this.name = name; this.sleepTime = sleepTime; } @Override public void run() { try { System.out.println(name+"开始运行,时间:"+System.currentTimeMillis()); //休眠时间 Thread.sleep(sleepTime); System.out.println(name+"结束运行,时间:"+System.currentTimeMillis()); } catch (Exception e) { e.printStackTrace(); } } } public static void main(String[] args) { ScheduledThreadPoolExecutor scheduledes = null; try { //创建线程任务池 scheduledes = new ScheduledThreadPoolExecutor(5); System.out.println("开始执行定时任务!"+System.currentTimeMillis()); //开始执行任务 ScheduledFuture FutureA = scheduledes.scheduleWithFixedDelay( new MyRunnable("任务1",2000),2,1, TimeUnit.SECONDS); ScheduledFuture FutureB = scheduledes.scheduleWithFixedDelay( new MyRunnable("任务2",1000),2,1, TimeUnit.SECONDS); ScheduledFuture FutureC = scheduledes.scheduleWithFixedDelay( new MyRunnable("任务3",1000),2,1, TimeUnit.SECONDS); ScheduledFuture FutureD = scheduledes.scheduleWithFixedDelay( new MyRunnable("任务4",1000),2,1, TimeUnit.SECONDS); //等待3s Thread.sleep(3000); //队列中不清空取消的任务 scheduledes.setRemoveOnCancelPolicy(false); //取消FutureA任务,正在运行的也取消 FutureA.cancel(true); //获取队列长度 BlockingQueue que = scheduledes.getQueue(); Iterator iterator = que.iterator(); System.out.println("现在队列长度:" + que.size()); while (iterator.hasNext()){ System.out.println("队列中任务:" + iterator.next()); } System.out.println("结束执行定时任务!"+System.currentTimeMillis()); } catch (Exception e) { e.printStackTrace(); } } } 开始执行定时任务!1596597288895 任务1开始运行,时间:1596597290899 任务2开始运行,时间:1596597290907 任务3开始运行,时间:1596597290930 任务4开始运行,时间:1596597290934 任务2结束运行,时间:1596597291910 任务4结束运行,时间:1596597291951 java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at com.minxl.demo.multiThread.ScheduledExecutorServiceDemo$MyRunnable.run(ScheduledExecutorServiceDemo.java:28) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) 任务3结束运行,时间:1596597291979 现在队列长度:3 队列中任务:java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@5451c3a8 队列中任务:java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@2626b418 队列中任务:java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@5a07e868 结束执行定时任务!1596597291981 任务2开始运行,时间:1596597292910 任务4开始运行,时间:1596597292951 任务3开始运行,时间:1596597292980 任务2结束运行,时间:1596597293913 任务4结束运行,时间:1596597293951 任务3结束运行,时间:1596597293980 任务2开始运行,时间:1596597294914 任务4开始运行,时间:1596597294953 任务3开始运行,时间:1596597294981 任务2结束运行,时间:1596597295914 任务4结束运行,时间:1596597295953 任务3结束运行,时间:1596597295981 任务2开始运行,时间:1596597296915 任务4开始运行,时间:1596597296954 任务3开始运行,时间:1596597296982 任务2结束运行,时间:1596597297915 任务4结束运行,时间:1596597297954 任务3结束运行,时间:1596597297982 任务2开始运行,时间:1596597298927 任务4开始运行,时间:1596597298963 任务3开始运行,时间:1596597298983 任务2结束运行,时间:1596597299927 任务4结束运行,时间:1596597299964
上面实验抛异常了,因为cancel(true)结束了正在执行的任务1,任务1里有sleep代码,该代码的存在无法将线程的中断状态置为true。队列中任务变为3,是因为抛异常导致,正常应该还是4。
介绍了基于线程池ThreadPoolExecutor的ScheduledThreadPoolExecutor 计划任务执行池对象,使用此类可以高效的实现计划任务线程 ,不再重复创建Thread 对象,提高了运行效率,此类也支持间隔运行的功能。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。