ThreadPoolExecutor创建线程池,java线程池corePoolSize
如何解决写爬虫IP受阻的问题?立即使用。
ThreadPool线程池
1.线程池1.1的优势。导言1.2。为什么要用线程池2.1。架构描述2.2。线程池的三个方法2.2.1.newFixedThreadPool(int)方法2.2.2。NewSingletThreadexector 2.2.3。NewCachedThreadPool 3。线程池执行器
1.线程池的优势
1.1.引言
类似于数据库线程池,如果没有数据库连接池,那么数据库的每个连接池都需要是新的才能获得连接池。重复的连接和释放操作会消耗大量的系统资源。我们可以使用数据库连接池,直接从池中获取连接池。
同样,在没有线程池之前,我们也是用new Thread.start()来获取线程。现在不需要new,可以实现重用,让我们的系统更高效。
1.2.为什么要使用线程池
示例:
10年前单核CPU计算机,假多线程,像马戏团小丑一样打多个球,CPU需要来回切换。现在是多核电脑,多线程运行在独立的CPU上,不用切换就很高效。线程池的优势:
线程池做的工作只要是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,超出数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行。
其主要特点是:
线程复用控制最大并发数管理线程优点:
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的销耗。第二:提高响应速度。当任务到达时,任务可以不需要等待线程创建就能立即执行。第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会销耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。2.线程池的使用
2.1.架构说明
Executor 框架是什么?
Java就是这么描述的。
执行提交的可运行任务的对象。该接口提供了一种将任务提交与每个任务将如何运行的机制相分离的方式,包括线程使用、调度等细节。通常使用执行器而不是显式创建线程。
执行提交的Runnable任务的对象。该接口提供了一种提交任务以及如何运行每个任务的机制,包括详细的信息使用和线程调度。通常使用执行器,而不是显式创建线程。
Java中的线程池是通过Executor的框架实现的,这个框架使用了Executor、Executors、ExecutorService、ThreadPoolExecutor等几个类。而我们常用的接口是ExecutorService子接口,Executors是线程的工具类(类似数组的工具类Arrays,集合的工具类Collections)。ThreadPoolExecutor是这些类的重点。我们可以通过辅助工具类Executors拿到ThreadPoolExecutor线程池
每个类别的详细描述如下:
Executor所有线程池的接口,只有一个方法,该接口定义执行Runnable任务的方式ExecutorService 增加Executor的行为,是Executor实现类的最直接的接口,该接口定义提供对Executor的服务Executors 线程池工厂类,提供了一系列工厂方法用于创建线程池,返回的线程池都实现了
ScheduledExecutorService:一个定时调度接口。
AbstractExecutorService执行框架抽象类。ThreadPoolExecutor JDK中线程池的具体实现,一般用的各种线程池都是基于这个类实现的
2.2.线程池的三大方法
2.2.1.newFixedThreadPool(int)方法
exectors . newfixedthreadpool(int)-执行长期任务性能好,创建一个线程池,一池有N个固定的线程,有固定线程数的线程
公共静态void main(String[] args) {
//5个接受线程的池类似于5个接受窗口的库。不管你现在有多少根线,只有五根。
ExecutorService thread pool=executors . newfixedthreadpool(5);
尝试{
//模拟10个客户来银行办理业务。目前池内有5名工作人员提供服务。
for(int I=1;i=10i ){
//execute方法中有一个参数,参数类型是runnable,Runnable是一个函数接口,可以使用lambda表达式,Runnable是10个客户。
threadPool.execute(()-{
system . out . println(thread . current thread()。getname() \ tHandling business );
});
}
} catch(异常e) {
//TODO:处理异常
}最后{
thread pool . shut down();
}
}
可以看到执行结果。池中有五个线程,相当于五个工作人员提供服务和处理业务。图2中的1号窗口已经办理了两笔交易,银行的受理窗口可以多次重复使用。不一定每个人都要处理两次,但是谁处理的快,谁处理的多。
当我们在线程执行期间添加400ms的延迟时,我们可以看到效果。
公共静态void main(String[] args) {
//5个接受线程的池类似于5个接受窗口的库。不管你现在有多少根线,只有五根。
ExecutorService thread pool=executors . newfixedthreadpool(5);
尝试{
//模拟10个客户来银行办理业务。目前池内有5名工作人员提供服务。
for(int I=1;i=10i ){
//execute方法中有一个参数,参数类型是runnable,Runnable是一个函数接口,可以使用lambda表达式,Runnable是10个客户。
threadPool.execute(()-{
system . out . println(thread . current thread()。getname() \ tHandling business );
});
尝试{
时间单位。毫秒睡眠(400);
} catch(异常e) {
//TODO:处理异常
e。printstacktrace();
}
}
} catch(异常e) {
//TODO:处理异常
}最后{
线程池。关闭();
}
}
此时说明网络拥堵的情况下或者办理业务比较慢,则线程池办理业务任务分配情况比较平均。
2.2.2.newSingleThreadExector
Exectors.newSingleThreadExector()一个任务一个任务的执行,一池一线程
公共静态void main(String[] args) {
//一池一个工作线程,类似一个银行有一个受理窗口
ExecutorService线程池=执行者。newsinglethreadexecutor();
尝试{
//模拟有10个顾客过来银行办理业务
for(int I=1;i=10i ){
//执行方法里面有一个参数,参数类型是可运行的,可运行的是函数式接口,可以用希腊字母的第11个表达式,并且可追捕的就是这10个顾客
threadPool.execute(()-{
系统。出去。println(线程。当前线程().getName() \t办理业务);
});
}
} catch(异常e) {
//TODO:处理异常
}最后{
线程池。关闭();
}}
2.2.3.newCachedThreadPool
执行人。newcachedthreadpool()执行很多短期异步任务,线程池根据需要创建新线程,但在先前构建的线程可用时将重用他们。可扩容,遇强则强。一池n线程,可扩容,可伸缩,缓存缓存的意思
那么池的数量应该设置多少呢,如果银行只有一个窗口,那么当人来得太多了,就忙不过来。如果银行有很多个窗口,但是人来的少,此时又显得浪费资源。那么如何该合理安排呢?这就需要用到newCachedThreadPool()方法,可扩容,可伸缩
公共静态void main(String[] args) {
//一池一个工作线程,类似一个银行有n个受理窗口
ExecutorService线程池=执行者。newcachedthreadpool();
尝试{
//模拟有10个顾客过来银行办理业务
for(int I=1;i=10i ){
//执行方法里面有一个参数,参数类型是可运行的,可运行的是函数式接口,可以用希腊字母的第11个表达式,并且可追捕的就是这10个顾客
threadPool.execute(()-{
系统。出去。println(线程。当前线程().getName() \t办理业务);
});
}
} catch(异常e) {
//TODO:处理异常
}最后{
线程池。关闭();
}
}
公共静态void main(String[] args) {
//一池一个工作线程,类似一个银行有n个受理窗口
ExecutorService线程池=执行者。newcachedthreadpool();
尝试{
//模拟有10个顾客过来银行办理业务
for(int I=1;i=10i ){
试试{时间单位.秒。睡眠;} catch(中断异常e){ e . printstacktrace();}
//执行方法里面有一个参数,参数类型是可运行的,可运行的是函数式接口,可以用希腊字母的第11个表达式,并且可追捕的就是这10个顾客
threadPool.execute(()-{
系统。出去。println(线程。当前线程().getName() \t办理业务);
});
}
} catch(异常e) {
//TODO:处理异常
}最后{
线程池。关闭();
}
}
3.ThreadPoolExecutor底层原理
newFixedThreadPool底层源代码
public static ExecutorService newFixedThreadPool(int nThreads){
返回新的ThreadPoolExecutor(nThreads,nThreads,
0L,时间单位。毫秒,
new LinkedBlockingQueueRunnable());
}可以看到,底层的参数包含队列阻塞队列。
新信号线程执行程序底层源代码
public static ExecutorService newSingleThreadExecutor(){
返回新的FinalizableDelegatedExecutorService
(新的ThreadPoolExecutor(1,1,
0L,时间单位。毫秒,
new LinkedBlockingQueueRunnable()));
}newCachedThreadPool底层源代码
public static ExecutorService newCachedThreadPool(){
返回新的ThreadPoolExecutor(0,整数MAX_VALUE,
60L,时间单位。秒,
new SynchronousQueueRunnable());
}同步队列这个阻塞队列是单一版阻塞队列,阻塞队列的容量为1.
这3个方法其实都共同返回了一个对象,即ThreadPoolExecutor的对象。
4.线程池7大重要参数
ThreadPoolExecutor的构造函数
公共ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
长keepAliveTime,
时间单位,
BlockingQueueRunnable工作队列,
线程工厂线程工厂,
RejectedExecutionHandler处理程序){
if (corePoolSize 0
maximumPoolSize=0
maximumPoolSize corePoolSize
keepAliveTime 0)
抛出新的IllegalArgumentException();
if(work queue==null thread factory==null handler==null)
抛出新的NullPointerException();
this . ACC=system . getsecuritymanager()==null?
空:
access controller . get context();
this . corePoolSize=corePoolSize;
this . maximum poolsize=maximum poolsize;
this . work queue=work queue;
this . keepAliveTime=unit . tonanos(keepAliveTime);
this . thread factory=thread factory;
this.handler=handler
}以上int corepoolsize,int maximumpoolsize,long keepalive time,time unit unit,blocking queue工作队列,threadfactory threadfactory,
RejectedExecutionHandler处理程序是我们的七个线程参数。
以上是ThreadPoolExecutor类的构造方法,有七个参数:
1)corePoolSize:线程池中的常驻核心线程数,简称核数。
例如,线程池可以视为银行网点。只要银行开门,至少要有一个人值班。这称为常驻核心线程的数量。例如,如果一家银行从周一到周五拥有所有五个网点,则从周一到周五的常驻核心线程数为5。如果今天业务不那么频繁,窗口为1,那么今天驻留的核心线程数为1。
2)maxImumPoolSize:线程池中能够容纳同时执行的最大线程数,此值必须大于等于1
3)keepAliveTime:多余的空闲线程的存活时间,当前池中线程数量超过corePoolSize时,当空闲时间达到keepAliveTime时,多余线程会被销毁直到剩下corePoolSize为止
如果线程池中有常驻线程和最大线程数,说明我们通常使用常驻线程。当工作紧张时,它会扩展到最大线程数。如果业务宕机,我们设置冗余空闲线程的生存时间,比如30s。如果30s内没有多余的请求进来,有些银行会关闭窗口,所以它不仅会扩大,还会缩小。
4)unit:keepAliveTime的单位
单位:秒、毫秒和微秒。
5)workQueue:任务队列,被提交但尚未被执行的任务
这是一个阻塞的队列,比如银行,只有三个受理窗口,四个客户。这个被堵的队列就是银行的等候区,客户来了也不能让他走。窗口的数量控制着并发线程的数量。
6)threadFactory:表示生成线程池中工作线程的线程工厂,用于创建线程,一般默认即可
线程是以统一的方式创建的。线程池中已经有新的线程,这些线程是由线程池工厂产生的。
7)handler:拒绝策略,表示当前队列满了,并且工作线程大于等于线程池的最大线程数(maximumPoolSize)时如何来拒绝请求执行的runnable的策略
比如今天银行客流高峰,三个窗口都满了,等候区也满了。我们没有选择继续拉人,因为不安全。我们选择了婉言拒绝。
在下一节中,我们将介绍线程池是如何在底层工作的。
以上是Java对ThreadPool线程池的详细解释。更多请关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。