14.9.2 预定执行
假设我们需要定时执行任务,可考虑使用下列方式:
🌽 创建线程池:ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
🌽 添加定时线程:
按开始时间间隔下一个开始时间:executorService.scheduleAtFixedRate(new MyRunnable(),0,5, TimeUnit.SECONDS);
按结束时间间隔下一个开始时间:executorService.scheduleWithFixedDelay(new MyRunnable(),0,5, TimeUnit.SECONDS);
具体事例如下:
❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Main {
public static void main(String[] args) throws InterruptedException {
Main solution = new Main();
//1.多线程同一次选择哪个线程是随机的,不一定选谁,可能会重复选同一个线程(如果执行时间太短的话)
//2.rate强调开始时间的时间差,执行时间是严格按照period倍数累加的
//3.delay强调开始与结束的间隔,执行时间是之前结束后才计算的,受到线程执行时间的影响
executeFixedRate();
System.out.println("==============================================");
executeFixedDelay();
}
private static void executeFixedRate() throws InterruptedException {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
for(int i = 0; i < 5; i++){
executorService.scheduleAtFixedRate(new MyRunnable(),0,5, TimeUnit.SECONDS);
}
Thread.sleep(50000);
executorService.shutdown();
}
private static void executeFixedDelay() throws InterruptedException {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
for(int i = 0; i < 5; i++){
executorService.scheduleWithFixedDelay(new MyRunnable(),0,5, TimeUnit.SECONDS);
}
Thread.sleep(50000);
executorService.shutdown();
}
}
class MyRunnable implements Runnable{
Calendar c = Calendar.getInstance();
@Override
public void run() {
c.setTime(new Date());
System.out.println(Thread.currentThread().getName()+":开始时间:"+c.getTime()+"的开始执行");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":开始时间:"+c.getTime()+"的执行结束");
}
}
❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤
执行结果:
🚗🚓🚕🛺🚙🚌🚐🚎🚑🚒🚚🚛🚜🚘🚔🚖🚍🦽🦼🛹🚲🛴🛵🏍
🌽 scheduleAtFixedRate 下一个执行的开始时间由上一个开始时间决定
🌽 scheduleWithFixedDelay 下一个执行的开始时间由上一个结束时间决定
线程跑太快的情况,可能一次执行会多次执行同一个线程(把Runnable的sleep删掉试试)
scheduleAtFixedRate:
scheduleWithFixedDelay:
延迟时间由上一个线程结束时间算起,距离第一个开始时间不是整点
🚗🚓🚕🛺🚙🚌🚐🚎🚑🚒🚚🚛🚜🚘🚔🚖🚍🦽🦼🛹🚲🛴🛵🏍
14.9.3 控制任务组
线程池方法:
🌽 shutdownNow 取消所有线程任务
🌽 invokeAny 有任何一个提交则结束
其实看到 shutdownNow 的一刻,我是懵的,这和 shutdown有什么区别呢?然后开始读注释,先看 shutdown:
按照提交执行顺序关闭任务,新的任务不会被接受。关了以后进行其他操作是无效的。
这个方法不会等已提交的任务执行完,如果要等已提交任务执行完再关闭,可以用 awaitTermination
整理如下:
按顺序关闭线程池中的线程任务
关闭以后不能执行新任务
没执行完的任务会强制关闭,不会等任务执行完再关闭
然后看 shutdownNow:
尝试停止所有正在执行的任务,停止等待执行的线程进程,然后返回等待执行的线程列表。
这个方法不会等待正在执行的线程停止。如果需要如果要等已提交任务执行完再关闭,可以用 awaitTermination
不能保证尝试中断一定能停止正在执行的线程。 例如调用了 Thread 类的 interrupted,然后响应结果是失败的任务可能不能中断。
返回:等待执行还未执行的任务列表
整理如下:
🌽 关闭正在执行的线程任务
🌽 返回等待执行的线程列表
🌽 关闭正在执行的线程任务可能会失败
不着急,再看看代码,注释可能不全,但是代码一定最为直接真实:
两者的区别:
🌽 shutdown 没有返回值,shutdownNow 返回待执行列表
🌽 shutdown SHUTDOWN状态,shutdownNow STOP 状态
🌽 shutdown 中断空闲线程,shutdownNow 中断所有线程
ThreadPoolExecutor 这个类有状态说明和声明周期变更说明,我们慢慢看
各种状态:
🌽 RUNNING: 接收新任务,执行队列任务
🌽 SHUTDOWN: 不接收新任务,但是执行队列任务
🌽 STOP: 不接收新任务,不执行队列任务,中断正在执行的任务
🌽 TIDYING:所有任务都结束了,工作线程数目为0,线程状态转换为 TIDYING,调用 terminated() 钩子方法
🌽 TERMINATED: terminated() 方法调用完成
状态转换:
🌽 RUNNING -> SHUTDOWN: 使用 shutdown() 方法变更,可能会隐式调用 finalize()
🌽 RUNING/SHUTDOWN -> STOP: 使用 shutdownNow() 方法
🌽 SHUTDOWN->TIDYING: 等待队列和线程池都空了(执行完了)
🌽 STOP -> TIDYDING: 线程池为空(停完了)
🌽 TIDYING ->TERMINATED: 当钩子方法 terminated() 方法调用并执行完毕时
还是比较抽象,玩个扔骰子的小游戏吧,扔到6的线程就结束,否则一直扔:
❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤
import java.util.*;
import java.util.concurrent.*;
public class Main {
public static void main(String[] args) throws InterruptedException, ExecutionException {
Main solution = new Main();
ExecutorService executorService = Executors.newFixedThreadPool(5);
for(int i = 0; i < 10; i++){
executorService.submit(new MyCallable("thread"+i));
}
Thread.sleep(1000);
executorService.shutdown();
System.out.println("=======");
}
}
class MyCallable implements Callable<String> {
Random r = new Random();
private String name;
public MyCallable(String threadName){
name = threadName;
}
@Override
public String call() throws Exception {
Date start = new Date();
System.out.println(name+"开始扔骰子,时间是:"+start);
while (getNum()!=6){
Thread.sleep(1000);
}
Date end = new Date();
System.out.println(name+"扔到了6!时间是:"+end);
return name+"共耗时"+((end.getTime()-start.getTime())/1000.0)+"秒";
}
private int getNum(){
return r.nextInt(6)+1;
}
}
❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤
以下为某次执行得到的结果:
🚗🚓🚕🛺🚙🚌🚐🚎🚑🚒🚚🚛🚜🚘🚔🚖🚍🦽🦼🛹🚲🛴🛵🏍
🚗🚓🚕🛺🚙🚌🚐🚎🚑🚒🚚🚛🚜🚘🚔🚖🚍🦽🦼🛹🚲🛴🛵🏍
我们注意到,shutdown()调用之后,线程还在执行,执行结束后才完全停止,把shutdown()改成shutdownNow()试试:
🚗🚓🚕🛺🚙🚌🚐🚎🚑🚒🚚🚛🚜🚘🚔🚖🚍🦽🦼🛹🚲🛴🛵🏍
🚗🚓🚕🛺🚙🚌🚐🚎🚑🚒🚚🚛🚜🚘🚔🚖🚍🦽🦼🛹🚲🛴🛵🏍
立刻关闭了,即使是正在执行的线程也关了。
那假设,我希望每个线程执行完了,给主线程一个反馈,按顺序报告,执行时间,该怎么做呢?可以用 ExecutorCompletionService,如下:
❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤
import java.util.*;
import java.util.concurrent.*;
public class Main {
public static void main(String[] args) throws InterruptedException, ExecutionException {
Main solution = new Main();
ExecutorService executorService = Executors.newCachedThreadPool();
ExecutorCompletionService<String> completionService = new ExecutorCompletionService<>(executorService);
List<MyCallable> list = new ArrayList<>();
for(int i = 0; i < 10; i++){
MyCallable call = new MyCallable();
list.add(call);
}
for(int i = 0; i < list.size(); i++){
completionService.submit(list.get(i));
}
Thread.sleep(3000);
for(int i = 0; i < list.size(); i++){
System.out.println(completionService.take().get());
}
executorService.shutdown();
}
}
class MyCallable implements Callable<String> {
Random r = new Random();
@Override
public String call() throws Exception {
Date start = new Date();
System.out.println(Thread.currentThread().getName()+"开始扔骰子,时间是:"+start);
while (getNum()!=6){
Thread.sleep(1000);
}
Date end = new Date();
System.out.println(Thread.currentThread().getName()+"扔到了6!时间是:"+end);
return Thread.currentThread().getName()+"共耗时"+((end.getTime()-start.getTime())/1000.0)+"秒";
}
private int getNum(){
return r.nextInt(6)+1;
}
}
❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤
执行结果:
🚗🚓🚕🛺🚙🚌🚐🚎🚑🚒🚚🚛🚜🚘🚔🚖🚍🦽🦼🛹🚲🛴🛵🏍
🚗🚓🚕🛺🚙🚌🚐🚎🚑🚒🚚🚛🚜🚘🚔🚖🚍🦽🦼🛹🚲🛴🛵🏍
总结:
🌽 scheduleAtFixedRate 和 scheduleWithFixedDelay 区别是一个按开始时间间隔,一个按结束时间间隔
🌽 Shutdown()与shutdownNow() 的区别是是否等当前线程执行完毕
🌽 线程池的生命周期有 RUNNING, SHUTDOWN, STOP, TINYING, TERMINATED
🌽 ExecutorCompletionService 可用于按时间接收结束执行的线程返回值
相关内容:选择 《Java核心技术 卷1》查找相关笔记
评论🌹点赞👍收藏✨关注👀,是送给作者最好的礼物,愿我们共同学习,一起进步
公众号 钰娘娘知识汇总