既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
运行结果:
3.2 带缓存的线程池(Executors.newCachedThreadPool)
线程池会根据任务数创建线程,并且在一定时间内可以重复使用这些线程。
示例代码:
public class ThreadPoolDemo4 {
public static void main(String[] args) {
// 创建线程池
ExecutorService service = Executors.newCachedThreadPool();
for (int i = 0; i < 1000; i++) {
int finalI = i;
service.submit(() -> System.out.println("i:" + finalI + " 线程名称:" + Thread.currentThread().getName()));
}
}
}
运行结果:
该方式适用于短时间有且有大量任务的场景,它的缺点是可能占用很多资源。
3.3 执行定时任务(Executors.newSingleThreadExecutor)
3.3.1 延迟执行(1次)
示例代码:
public class ThreadPoolDemo12 {
public static void main(String[] args) {
ScheduledExecutorService threadPool =
Executors.newScheduledThreadPool(10);
// 定时任务
System.out.println("设置定时任务:" + new Date());
// 延迟 n 秒后执⾏(只执⾏⼀次)
threadPool.schedule(() -> System.out.println("schedule:" + new Date()), 2, TimeUnit.SECONDS);
}
}
执行结果:
延迟 2s 后执行一次。
3.3.2 固定频率执行( scheduleAtFixedRate)
示例代码:
public class ThreadPoolDemo13 {
public static void main(String[] args) {
ScheduledExecutorService threadPool =
Executors.newScheduledThreadPool(10);
// 定时任务
System.out.println("设置定时任务:" + new Date());
threadPool.scheduleAtFixedRate(() ->
System.out.println("scheduleAtFixedRate:" + new Date()), 3, 2, TimeUnit.SECONDS);
}
}
运行结果:
延迟3s后执行,之后每2s执行一次。
参数解释:
- 参数1:执行任务;
- 参数2:延迟 n 秒后执行;
- 参数3:执行定时任务的频率;
- 参数4:配合参数3使用的时间单位。
3.3.3 scheduleAtFixedRate VS scheduleWithFixedDelay
scheduleAtFixedRate 是以上⼀次任务的开始时间,作为下次定时任务的参考时间的(参考时间+延迟任务=任务执行)。
示例代码:
public class ThreadPoolDemo5 {
public static void main(String[] args) {
// 创建线程池
ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
System.out.println("添加任务执行时间:" + LocalDateTime.now());
// 2s之后开始执行定时任务,定时任务每隔4s执行一次
service.scheduleAtFixedRate(() -> {
System.out.println("执行了任务:" + LocalDateTime.now());
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, 2, 4, TimeUnit.SECONDS);
}
}
运行结果:
2s后开始执行定时任务,每隔5s执行一次。
设置的是每隔4秒执行一次定时任务,为什么实际上是5s执行一次呢?
注意,如果执行任务时间大于设置的定时任务执行时间,那么此方法会以执行任务的时间为准,简而言之,就是哪个时间长就以哪个时间作为定时任务执行的周期。
scheduleWithFixedDelay 是以上⼀次任务的结束时间,作为下次定时任务的参考时间的。
示例代码:
public class ThreadPoolDemo5 {
public static void main(String[] args) {
// 创建线程池
ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
System.out.println("添加任务执行时间:" + LocalDateTime.now());
// 2s之后开始执行定时任务,每次执行间隔4秒
service.scheduleWithFixedDelay(() -> {
System.out.println("执行了任务:" + LocalDateTime.now());
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, 2, 4, TimeUnit.SECONDS);
}
}
运行结果:
2s后开始执行任务,每隔9秒执行一次定时任务。
为什么这个也不是每隔4s执行一次,而是9s呢???
因为 scheduleWithFixedDelay 是以上⼀次任务的结束时间,作为下次定时任务的参考时间的,上个任务执行5s后,再延时4s执行延时任务。
3.4 定时任务单线程(Executors.newSingleThreadScheduledExecutor)
示例代码:
public class ThreadPoolDemo6 {
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
System.out.println("添加任务时间:" + LocalDateTime.now());
service.schedule(() -> System.out.println("执行任务:" + LocalDateTime.now()), 2, TimeUnit.SECONDS);
}
}
运行结果:
3.5 单线程线程池(Executors.newSingleThreadExecutor)
示例代码:
public class ThreadPoolDemo7 {
public static void main(String[] args) {
ExecutorService service = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
int finalI = i;
service.submit(() -> System.out.println("任务:" + finalI + ", 线程名:" + Thread.currentThread().getName()));
}
}
}
运行结果:
单线程的线程池有什么意义呢?
- 自定义拒绝策略;
- 提供了任务队列和任务管理的功能。
3.6 根据当前CPU生成线程池(Executors.newWorkStealingPool)
示例代码:
public class ThreadPoolDemo8 {
public static void main(String[] args) {
ExecutorService service = Executors.newWorkStealingPool();
for (int i = 0; i < 100; i++) {
service.submit(() -> System.out.println("线程名:" + Thread.currentThread().getName()));
}
while (!service.isTerminated()){
}
}
}
运行结果:
3.7 手动方式(ThreadPoolExecutor)
3.7.1 创建忽略最新任务的线程池
示例代码:
public class ThreadPoolDemo11 {
public static void main(String[] args) {
ThreadFactory factory = r -> {
Thread thread = new Thread(r);
return thread;
};
// 手动方式创建线程池
ThreadPoolExecutor executor =
new ThreadPoolExecutor(2, 2, 10, TimeUnit.SECONDS,
new LinkedBlockingDeque<>(2), factory, new ThreadPoolExecutor.DiscardPolicy());
for (int i = 0; i < 5; i++) {
int finalI = i;
executor.submit(() -> {
try {
Thread.sleep(finalI * 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "执行任务" + finalI);
});
}
// 终止线程池
executor.shutdown();
}
}
运行结果:
3.7.2 ThreadPoolExecutor 参数说明
- corePoolSize:核心线程数,可以大致理解为长期驻留的线程数目(除非设置了allowCoreThreadTimeOut)。对于不同的线程池,这个值可能会有很大区别,比如newFixedThreadPool 会将其设置为 nThreads,而对于 newCachedThreadPool 则是为 0。
- maximumPoolSize:顾名思义,就是线程不够时能够创建的最⼤线程数。同样进⾏对比,对于newFixedThreadPool,当然就是 nThreads,因为其要求是固定大小,而 newCachedThreadPool 则是 Integer.MAX_VALUE。
- keepAliveTime:空闲线程的保活时间,如果线程的空闲时间超过这个值,那么将会被关闭。注意此值生效条件必须满足:空闲时间超过这个值,并且线程池中的线程数少于等于核⼼线程数corePoolSize。当然核心线程默认是不会关闭的,除非设置了allowCoreThreadTimeOut(true)那么核心线程也可以被回收。
- TimeUnit:时间单位。
- BlockingQueue:任务队列,用于存储线程池的待执行任务的。
- threadFactory:⽤于生成线程,⼀般我们可以⽤默认的就可以了。
- handler:当线程池已经满了,但是又有新的任务提交的时候,该采取什么策略由这个来指定。有几种方式可供选择,像抛出异常、直接拒绝然后返回等,也可以自己实现相应的接口实现自己的逻辑。
3.7.3 线程池执行流程
3.7.4 拒绝策略(5种(4(JDK提供的) + 1(自定义拒绝策略)))
JDK提供的四种拒绝策略:
- DiscardPolicy : 忽略旧任务(队列第一个任务)
- AbortPolicy : 提示异常,拒绝执行(默认的拒绝策略)
- CallerRunsPolicy : 使用调用线程池的线程来执行任务
- DiscardOldestPolicy : 忽略最新任务
自定义拒绝策略:
示例代码:
public class ThreadPoolDemo10 {
public static void main(String[] args) {
ThreadFactory factory = r -> {
Thread thread = new Thread(r);
return thread;
};
// 手动方式创建线程池
ThreadPoolExecutor executor =
new ThreadPoolExecutor(2, 2, 10, TimeUnit.SECONDS,
new LinkedBlockingDeque<>(2), factory,
new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// 自定义拒绝策略
System.out.println("自定义拒绝策略");
}
});
for (int i = 0; i < 5; i++) {
int finalI = i;
executor.submit(() -> {
try {
Thread.sleep(finalI * 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "执行任务" + finalI);
});
}
// 终止线程池
executor.shutdown();
}
}
运行结果:
4. 线程池状态
查看 ThreadPoolExecutor 源码可知线程的状态如下:
- RUNNING:这是最正常的状态:接受新的任务,处理等待队列中的任务;
- SHUTDOWN:不接受新的任务提交,但是会继续处理等待队列中的任务;
- STOP:不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程;
- TIDYING:所有的任务都销毁了,workCount 为 0。线程池的状态在转换为 TIDYING 状态时,会执行钩子方法 terminated();
- TERMINATED:terminated() 方法结束后,线程池的状态就会变成这个。
各个状态的转换过程有以下几种:
- RUNNING -> SHUTDOWN:当调用了 shutdown() 后,会发生这个状态转换,这也是最重要的;
- (RUNNING or SHUTDOWN) -> STOP:当调⽤ shutdownNow() 后,会发⽣这个状态转换;
- SHUTDOWN -> TIDYING:当任务队列和线程池都清空后,会由 SHUTDOWN 转换为 TIDYING;
- STOP -> TIDYING:当任务队列清空后,发⽣这个转换;
- TIDYING -> TERMINATED:这个前面说了,当 terminated() 方法结束后。
shutdown VS shutdownNow :
- shutdown 执行时线程池终止接收新任务,并且会将任务队列中的任务处理完;
- shutdownNow 执行时线程池终止接收新任务,并且会给终止执行任务队列中的任务。
shutdown :
public class ThreadPoolDemo10 {
public static void main(String[] args) {
ThreadFactory factory = r -> {
Thread thread = new Thread(r);
return thread;
![img](https://img-blog.csdnimg.cn/img_convert/093bf4e459586d303c0485e75bad0af6.png)
![img](https://img-blog.csdnimg.cn/img_convert/18cea2bdc80c45b0a8b5f6ba8f72a7f1.png)
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**
**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**
hutdown :**
public class ThreadPoolDemo10 {
public static void main(String[] args) {
ThreadFactory factory = r -> {
Thread thread = new Thread®;
return thread;
[外链图片转存中…(img-Ro3CZWqa-1715693536359)]
[外链图片转存中…(img-KVSVtobd-1715693536359)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新