笔记 第14章 多线程(11)定时执行线程池与线程池生命周期_java

笔记 第14章 多线程(11)定时执行线程池与线程池生命周期_i++_02

 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:

笔记 第14章 多线程(11)定时执行线程池与线程池生命周期_线程池_03

scheduleWithFixedDelay:

延迟时间由上一个线程结束时间算起,距离第一个开始时间不是整点

笔记 第14章 多线程(11)定时执行线程池与线程池生命周期_开发语言_04

🚗🚓🚕🛺🚙🚌🚐🚎🚑🚒🚚🚛🚜🚘🚔🚖🚍🦽🦼🛹🚲🛴🛵🏍

笔记 第14章 多线程(11)定时执行线程池与线程池生命周期_i++_05

 14.9.3 控制任务组

线程池方法:

🌽  shutdownNow 取消所有线程任务

🌽  invokeAny 有任何一个提交则结束

其实看到 shutdownNow 的一刻,我是懵的,这和 shutdown有什么区别呢?然后开始读注释,先看 shutdown:

笔记 第14章 多线程(11)定时执行线程池与线程池生命周期_i++_06

按照提交执行顺序关闭任务,新的任务不会被接受。关了以后进行其他操作是无效的。

这个方法不会等已提交的任务执行完,如果要等已提交任务执行完再关闭,可以用 awaitTermination

整理如下:

按顺序关闭线程池中的线程任务

关闭以后不能执行新任务

没执行完的任务会强制关闭,不会等任务执行完再关闭

然后看 shutdownNow:

笔记 第14章 多线程(11)定时执行线程池与线程池生命周期_i++_07

尝试停止所有正在执行的任务,停止等待执行的线程进程,然后返回等待执行的线程列表。

这个方法不会等待正在执行的线程停止。如果需要如果要等已提交任务执行完再关闭,可以用 awaitTermination

不能保证尝试中断一定能停止正在执行的线程。 例如调用了 Thread 类的 interrupted,然后响应结果是失败的任务可能不能中断。

返回:等待执行还未执行的任务列表

整理如下:

🌽 关闭正在执行的线程任务

🌽 返回等待执行的线程列表

🌽 关闭正在执行的线程任务可能会失败

不着急,再看看代码,注释可能不全,但是代码一定最为直接真实:

笔记 第14章 多线程(11)定时执行线程池与线程池生命周期_java_08

两者的区别:

🌽 shutdown 没有返回值,shutdownNow 返回待执行列表

🌽 shutdown SHUTDOWN状态,shutdownNow STOP 状态

🌽 shutdown 中断空闲线程,shutdownNow 中断所有线程

笔记 第14章 多线程(11)定时执行线程池与线程池生命周期_i++_09

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() 方法调用并执行完毕时

笔记 第14章 多线程(11)定时执行线程池与线程池生命周期_i++_10

还是比较抽象,玩个扔骰子的小游戏吧,扔到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;
}

}

❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤

以下为某次执行得到的结果:

🚗🚓🚕🛺🚙🚌🚐🚎🚑🚒🚚🚛🚜🚘🚔🚖🚍🦽🦼🛹🚲🛴🛵🏍

笔记 第14章 多线程(11)定时执行线程池与线程池生命周期_java_11

🚗🚓🚕🛺🚙🚌🚐🚎🚑🚒🚚🚛🚜🚘🚔🚖🚍🦽🦼🛹🚲🛴🛵🏍

我们注意到,shutdown()调用之后,线程还在执行,执行结束后才完全停止,把shutdown()改成shutdownNow()试试:

🚗🚓🚕🛺🚙🚌🚐🚎🚑🚒🚚🚛🚜🚘🚔🚖🚍🦽🦼🛹🚲🛴🛵🏍

笔记 第14章 多线程(11)定时执行线程池与线程池生命周期_java_12

🚗🚓🚕🛺🚙🚌🚐🚎🚑🚒🚚🚛🚜🚘🚔🚖🚍🦽🦼🛹🚲🛴🛵🏍

立刻关闭了,即使是正在执行的线程也关了。

那假设,我希望每个线程执行完了,给主线程一个反馈,按顺序报告,执行时间,该怎么做呢?可以用 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;
}

}

❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤

执行结果:

🚗🚓🚕🛺🚙🚌🚐🚎🚑🚒🚚🚛🚜🚘🚔🚖🚍🦽🦼🛹🚲🛴🛵🏍

笔记 第14章 多线程(11)定时执行线程池与线程池生命周期_线程池_13

🚗🚓🚕🛺🚙🚌🚐🚎🚑🚒🚚🚛🚜🚘🚔🚖🚍🦽🦼🛹🚲🛴🛵🏍

总结:

🌽 scheduleAtFixedRate 和 scheduleWithFixedDelay 区别是一个按开始时间间隔,一个按结束时间间隔

🌽 Shutdown()与shutdownNow() 的区别是是否等当前线程执行完毕

🌽 线程池的生命周期有 RUNNING, SHUTDOWN, STOP, TINYING, TERMINATED

🌽 ExecutorCompletionService 可用于按时间接收结束执行的线程返回值

相关内容:选择 《Java核心技术 卷1》查找相关笔记

评论🌹点赞👍收藏✨关注👀,是送给作者最好的礼物,愿我们共同学习,一起进步

公众号 钰娘娘知识汇总 

笔记 第14章 多线程(11)定时执行线程池与线程池生命周期_i++_14