发现一件奇怪的事情:最近一句话总是浮现在我心头,也常常挂在嘴边。念念不忘,必有回响
,也许只是我对于秋招之后自己的安慰,我相信也更是一种激励。坚持就一定有收获,可能人在一生中一定会遇到挫折和低谷,咬着牙坚持下去。我写代码的样子可能很狼狈,但坚持下去的样子一定很酷。也特别相信一定会有一个好的归宿。祝你也祝我!
同时今天在这里也特别感谢在我学习期间帮助我的一些CSDN上认识的朋友和一些大佬,也同时感谢我的粉丝们的一路陪伴,自知我的博客其实写的深度和广度其实大多时候都不是很好,感谢你们的陪伴,希望我们都努力。以后我也会加强学习,分享给大家更多的干货。同时也欢迎大家踊跃和我交流。哈哈哈,互相帮助互相激励是一种愉快的学习和生活的方式。
好,进入正题:今天来浅谈一下多线程的线程池。
为什么要使用线程池?什么是池化思想?
其实谈到为什么使用线程池,就不得不谈到池化思想。
其实在程序运行过程中是占用的资源,池化思想就是用来优化资源利用的一种思想,池化思想的具体实现有甚多:如数据库连接池、个人感觉数据库中的缓存也是这种类似的思想只是具体的实现思想可能会有优化。
简单的说其实就是:我创建一个池子,其中事先创建一些资源,你需要资源的使用直接从池中取资源去利用(不需要自己去创建),利用完资源之后你再将资源放在池中。
减少资源消耗,速度提升,方便管理。
- 减少资源消耗:不需要每个人都去创建资源,而是将资源共用并且管理起来。
- 速度提升:在使用之前可能不需要再次去创建资源,而是“拿来主义”。
- 方便管理:我们可以控制线程的最大线程数。
常说的3大方法,7大参数,4种拒绝策略到底是什么?
谈到ava线程池3大方法,7大参数,4种拒绝策略,所有人都耳熟能详了,但是你真的了解3大方法,7大参数,4种拒绝策略?
接下来,详细介绍一下:
3大方法
3大方法指的是线程池创建的三种方式。
- singleThreadExecutor 创建单个线程
- newFixedThreadpool 创建固定线程个数的线程
- newCacheThreadpool 可伸缩,遇强则强,遇弱则弱
这里只例举了一个使用singleThreadExecutor创建线程池的方式。其他两种的创建线程的方式也是类似的。
- 线程池的执行方法是:execute或者submit
- execute方法和submit有什么不同?
- execute的方式来提交的话是没有返回值的
- submit的方式来提交的话是有返回值的
- 也就是说如果使用Runnable来创建的线程,那么可以使用execute和submit两种方式来提交
- 而使用Callable来创建线程的话就只能使用submit方法来提交
- execute方法和submit有什么不同?
- 线程池销毁的方法是shutdown
package jucTest;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author 雷雨
* @date 2020/12/17 22:38
*/
public class SingleThreadExecuorTest {
public static void main(String[] args) {
ExecutorService pool= Executors.newSingleThreadExecutor();
try{
for (int i = 0; i < 100; i++) {
pool.execute(()->{
System.out.println(Thread.currentThread().getName()+"感谢各位的关注啊");
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
//用于关闭线程池
pool.shutdown();
}
}
}
《阿里巴巴开发手册》中关于创建线程池的描述:
强制要求使用原生的ThreadPoolExecutor的方式来创建线程,避免造成OOM问题。
使用原生的ThreadPoolExecutor的方式来创建线程池就要求7大参数和了解4种拒绝策略。
7大参数
直接上代码,7大参数的说明都在代码中。
package jucTest;
import java.util.concurrent.*;
/**
* @author 雷雨
* @date 2020/12/17 23:02
* 使用原生创建线程池的方式来创建线程
*/
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
1, //核心线程数
3, //最大线程数
3, //超时时间
TimeUnit.SECONDS,//时间单位
new LinkedBlockingDeque<>(3),//阻塞队列
Executors.defaultThreadFactory(),//一个线程工厂,一般使用默认的就OK
new ThreadPoolExecutor.AbortPolicy()//一种拒绝策略
);
}
}
- 核心线程指的是:在线程中默认创建的线程
- 最大线程:见名知意指的就是最大的线程数
- 阻塞队列:用于核心线程池满时,让需要"服务"的线程等待的地方
- 拒绝策略:指的是最大线程池满,阻塞队列也满的时候,新来的线程如何处置。
画个图来强化理解:
假设有银行,核心线程指的就是常用的柜台(一直都会服务的柜台),最大线程指的是银行一共有多少个柜台,阻塞队列理解为排队等待区,拒绝策略就是如果银行满了,新来的人银行处置的策略。
- 平时使用的都是核心的线程,当阻塞队列满时,才会去启用最大线程池内部的其他的线程。
4种拒绝策略
- new ThreadPoolExecuor.AbortPolicy :不处理这个线程,并抛出异常
- new ThreadPoolExecuor.CallerRunsPolicy:这个多的线程不处理,而是交给创建这个线程的去处理
- new ThreadPoolExecuor.DiscardPolicy:不处理这个线程,也不抛出异常
- new ThreadPoolExecuor.DiscardOldestPolicy:尝试和最老的线程进行竞争,也不会抛出异常
将四种拒绝策略应用于银行的场景:
- AbortPolicy的处理策略就是:银行满了,那么新来的人就处理不了了,报出异常
- CallerRunsPolicy:银行满了,那么这个人是从哪里来的就去哪里,对应线程中就是在那个线程中创建的该线程就去找他去执行该线程。
- DiscardPolicy:银行满了,新来的人,我当看不见,不管他,不处理它,也不报异常
- DiscardOldestPolicy:银行满了,新来的人,我让他进来和最开始执行的线程竞争,谁竞争到谁去执行。