摘要:
直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。newSingleThreadExecutor(ThreadPoolExecutor)创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按
带着问题
阿里Java代码规范为什么不允许使用Executors快速创建线程池?
下面的代码输出是什么?
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1, //corePoolSize
100, //maximumPoolSize
100, //keepAliveTime
TimeUnit.SECONDS, //unit
new LinkedBlockingDeque<>(100));//workQueue
for (int i = 0; i < 5; i++) {
final int taskIndex = i;
executor.execute(() -> {
System.out.println(taskIndex);
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
A) 0 1 2 3 4 5
以查看Startup.cs文件,里面会注入IHostingEnvironment对象实例,其中里面的env.IsDevelopment就是通过这个值来判断当前工作环境是否是Development环境,
B) 0~5 顺序不一致输出5行
个资源集,任何池的作用都大同小异,主要是用来减少资源创建、初始化的系统开销。创建线程很“贵”吗?是的。创建线程的代价是昂贵的。我们都知道系统中的每个进程有自己独立的内存空间,而被称为轻量级进程的线程也
C) 0
dTimeOut为true才会退出。unit与keepAliveTime配合,设置keepAliveTime的单位,如:毫秒、秒。workQueue线程池中的任务队列。上面提到线程池的主要作用是复用线
基础
什么是线程池?
线程池可以通过池看出来是一个资源集,任何池的作用都大同小异,主要是用来减少资源创建、初始化的系统开销。
就是说等错误出现了以后我们才去测试相应的方法,很少见有谁使用postman把所有方法全部都跑一遍.幸运的是.微软在把.net升级到.netcore之后,asp.netcore可以搭建一个内存服务器来完
创建线程很“贵”吗?
是的。创建线程的代价是昂贵的。
ntityFramework.还有一些工具类的框架只是在开发环境中使用的,线上环境并不需要它.这个概念可能很容易理解,但是相信大家仍然看的一头蒙圈,为什么是Development,而不是Develop
我们都知道系统中的每个进程有自己独立的内存空间,而被称为轻量级进程的线程也是需要的。
;>(100));//workQueuefor(inti=0;i<5;i++){finalinttaskIndex=i;executor.execute(()->{System.ou
在JVM中默认一个线程需要使用256k~1M(取决于32位还是64位操作系统)的内存。(具体的数组我们不深究,因为随着JVM版本的变化这个默认值随时可能发生变更,我们只需要知道线程是需要占用内存的)
ionHandlerhandler)corePoolSize核心池大小,除非设置了allowCoreThreadTimeOut否则哪怕线程超过空闲时间,池中也要最少要保留这个数目的线程。需要注意的是,
除了内存还有更多吗?
许多文章会将上下文切换、CPU调度列入其中,这边不将线程调度列入是因为睡眠中的线程不会被调度(OS控制),如果不是睡眠中的线程那么是一定需要被调度的。
但在JVM中除了创建时的内存消耗,还会给GC带来压力,如果频繁创建线程那么相对的GC的时候也需要回收对应的线程。
asterpro,httpdebugger等工具进行测试.在这些工具中,postman相对好用,不但提供了测试数据的分组功能,还可以编写脚本来完成一些自动化工作,比如自动添加固定http头等.然而即便
线程池的机制?
可以看到线程池是一种重复利用线程的技术,线程池的主要机制就是保留一定的线程数在没有事情做的时候使之睡眠,当有活干的时候拿一个线程去运行。
这些牵扯到线程池实现的具体策略。
tentRoot此方法用于指定项目运行时资源文件的根目录,通常情况下是创建项目的目录,以上目录大家根据创建项目的路径来选择,不要盲目拷贝以上代码.UseEnvironment此方法用于指定项目的运行环
还有哪些常见的池?
线程池
连接池(数据库连接、TCP连接等)
BufferPool
......
Java中的线程池
UML图(Java 8)
可以看到真正的实现类有
ThreadPoolExecutor (1.5)
ForkJoinPool (1.7)
ScheduledThreadPoolExecutor (1.5)
今天我们主要谈谈 ThreadPoolExecutor 也是使用率较高的一个实现。
cutornewWorkStealingPoolForkJoinPoolThreadPoolExecutor首先我们看下ThreadPoolExecutor的完全构造函数publicThreadPoo
Executors提供的工厂方法
newCachedThreadPool (ThreadPoolExecutor)
池来处理呢?这边推荐大家使用newWorkStealingPool,也就是ForkJoinPool。采取了工作窃取的模式。后续会跟大家一起聊聊ForkJoinPool。系列目录在web项目里,我们把每
创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
看到BuildWebHost方法里面使用了WebHost类来创建一个默认WebHostBuilder,这个方法里面也有UseStartup方法,其实像亲朋ContentRoot,UseEnvironm
newFixedThreadPool (ThreadPoolExecutor)
池。此线程池支持定时以及周期性执行任务的需求。newSingleThreadScheduledExecutor(ScheduledThreadPoolExecutor)创建一个单线程用于定时以及周期性
创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
rnnewFinalizableDelegatedExecutorService(newThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,newLinke
newSingleThreadExecutor (ThreadPoolExecutor)
dAsStringAsync();Assert.Equal("Hello,World",responseStr);}}配置详解下面我们来详细分析这段代码我们先来看构造函数里的代码,
创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
获取结果,很容易造成死锁.可能大家在工作中也确实使用过xxx.Result来同步阻塞获取异步结果,并且能正确返回真,但是阻塞HttpClient里的异步方法很多时候都会造成死锁.至于原因大家可以查看这
newScheduledThreadPool (ScheduledThreadPoolExecutor)
池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。newSingleThreadExecutor(ThreadPoolExecutor)创建一个单线程的线
创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
nable>()理论上可以无限添加任务到线程池。publicstaticExecutorServicenewFixedThreadPool(intnThreads){returnnewThrea
newSingleThreadScheduledExecutor (ScheduledThreadPoolExecutor)
eMvc.Startup>();varserver=newTestServer(builder);_client=server.CreateClient();}[Fact]publicasync
创建一个单线程用于定时以及周期性执行任务的需求。
一个HttpClient对象,利用HttpClient对象我们便可以构建Http请求了.需要说明的是这个HttpClient并没什么特殊的,它就是System.net.http下的httpclient
newWorkStealingPool (1.8 ForkJoinPool)
CachedThreadPool(ThreadPoolExecutor)创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加
创建一个工作窃取
设置,有最优值吗?由于java对于协程的支持不友好,所以会大量依赖于线程池和线程。从而这个值没有最优推荐,需要根据业务需求情况来进行设置。不同的需求类型可以创建多个不同的线程池来执行。问题1:阿里开发
可以看到各种不同的工厂方法中使用的线程池实现类最终只有3个,对应关系如下:
Mvc项目里我们新建一个名为mvc20的测试类,代码如下publicclassmvc20{privatereadonlyHttpClient_client;publicmvc20(){varbuild
工厂方法
实现类
newCachedThreadPool
ThreadPoolExecutor
newFixedThreadPool
ThreadPoolExecutor
newSingleThreadExecutor
ThreadPoolExecutor
newScheduledThreadPool
ScheduledThreadPoolExecutor
newSingleThreadScheduledExecutor
ScheduledThreadPoolExecutor
newWorkStealingPool
ForkJoinPool
ThreadPoolExecutor
首先我们看下 ThreadPoolExecutor 的完全构造函数
webapi还是基本的http请求在.netcore里都作为一个中间件,要使用必须配置.回到测试方法里,这个startup文件直接指定为mvc项目里的startup文件即可,并且必须要这样,如果测试环
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize
的线程数在没有事情做的时候使之睡眠,当有活干的时候拿一个线程去运行。这些牵扯到线程池实现的具体策略。还有哪些常见的池?线程池连接池(数据库连接、TCP连接等)BufferPool......Java中
核心池大小,除非设置了 allowCoreThreadTimeOut 否则哪怕线程超过空闲时间,池中也要最少要保留这个数目的线程。
olicy直接丢弃当前任务。DiscardOldestPolicy将最旧的任务丢弃,将当前任务添加到队列。容易混淆的参数:corePoolSizemaximumPoolSizeworkQueue任务队
需要注意的是,corePoolSize所需的线程并不是立即创建的,需要在提交任务之后进行创建,所以如果有大量的缓存线程数可以先提交一个空任务让线程池将线程先创建出来,从而提升后续的执行效率。
lloWorldController:Controller{publicIActionResultHello(){returnContent("Hello,World");}}然后
maximumPoolSize
UseStartup此方法用于指定一个Startup文件,Startup文件在mvc5项目里也有,主要是关于中间件的配置,只是mvc5在启动的时候自动调用一个名为Startup的文件(mvc5项目里这
允许的最大线程数。
于32位还是64位操作系统)的内存。(具体的数组我们不深究,因为随着JVM版本的变化这个默认值随时可能发生变更,我们只需要知道线程是需要占用内存的)除了内存还有更多吗?许多文章会将上下文切换、CPU调
keepAliveTime
使用池中的线程来处理这些任务,所以我们需要一个任务队列。threadFactory当线程池判断需要新的线程时通过线程工程创建线程。handler执行被阻止时的处理程序,线程池无法处理。这个与任务队列相
空闲线程空闲存活时间,核心线程需要 allowCoreThreadTimeOut 为true才会退出。
.这样我们就在项目发布前可以先运行一下集成测试确保各方法都是可访问的,减少项目上线后出现一些简单错误的机率.下面我们介绍如何搭建集成测试环境产生我们使用VisualStudio自带的模板创建一个Asp
unit
ties里面的launchSettings.json里定义的.因此如果我们提供的参数是"Development",mvc项目启动的时候就把它和这个文件匹配,如果匹配成功则当前工作环
与 keepAliveTime 配合,设置 keepAliveTime 的单位,如:毫秒、秒。
d.sleep(Long.MAX_VALUE);}catch(InterruptedExceptione){e.printStackTrace();}});}A)012345B)0~5顺序不一致输出5
workQueue
交任务之后进行创建,所以如果有大量的缓存线程数可以先提交一个空任务让线程池将线程先创建出来,从而提升后续的执行效率。maximumPoolSize允许的最大线程数。keepAliveTime空闲线程空
线程池中的任务队列。上面提到线程池的主要作用是复用线程来处理任务,所以我们需要一个队列来存放需要执行的任务,在使用池中的线程来处理这些任务,所以我们需要一个任务队列。
法过多,管理起来很不方便.并且构造复杂json或者formdata数据更是让人心力绞悴,稍有一点格式错误就导致整个测试通不过,严重影响对错误点位置的判断.更为重要的一点是,我们不论是在页面中输入地址,
threadFactory
环境约定的默认环境,至于为什么需要指定工作环境,因为很多时候开发环境和线上环境中使用到的组件是不一样的,这在.netcore项目里尤其明显,比如说线上环境中我们使用的是正式的EntityFramewo
当线程池判断需要新的线程时通过线程工程创建线程。
newThreadPoolExecutor(1,//corePoolSize100,//maximumPoolSize100,//keepAliveTimeTimeUnit.SECONDS,//uni
handler
aximumPoolSize,longkeepAliveTime,TimeUnitunit,BlockingQueueworkQueue,ThreadFactoryth
执行被阻止时的处理程序,线程池无法处理。这个与任务队列相关,比如队列中可以指定队列大小,如果超过了这个大小该怎么办呢?JDK已经为我们考虑到了,并提供了4个默认实现。
过程缺失了http管道,很多时候我们还还需要把项目布在iis环境中或者在vs里启动iisexpress服务器进行集成测试.对于返回view的的方法我们通常是在浏览器中输入地址进行测试,对于返回数据的方
下列是JDK中默认携带的策略:
AbortPolicy (默认)
项目模板生成的方法确实是这样的,只是在.netcore2.0进行了更进一步的抽象,ContentRoot路径,默认工作环境都按照惯例提供,不再需要手动指定.但是测试项目的工作环境和mvc项目的运行环境
抛出 RejectedExecutionException 异常。
常.需要注意的是EnsureSuccessStatusCode请求返回的必须是200才能通过,201,204这样的状态码也会抛出异常.大家在开发过程中一定要根据实际情况来决定是否使用它.一般情况下前后
CallerRunsPolicy
cutorThreadPoolExecutornewScheduledThreadPoolScheduledThreadPoolExecutornewSingleThreadScheduledExec
调用当前线程池所在的线程去执行。
且任务队列未满时,将任务放入任务队列。"这个条件。所以后续添加的任务都会被堵塞。最后关于ThreadPoolExecutor的逻辑在实际使用的时候会有点奇怪,因为线程池中的线程并没有超过最大线
DiscardPolicy
;}publicstaticIWebHostBuildWebHost(string[]args)=>WebHost.CreateDefaultBuilder(args).UseStartup&l
直接丢弃当前任务。
string[]args).然后我们在visualstudio里对着项目右键点击属性,在Build标签下选择高级,弹出的对话框里语言版本下拉列表选择c#7.1或者更高(写本文时,VisualStudi
DiscardOldestPolicy
eThreadScheduledExecutor(ScheduledThreadPoolExecutor)创建一个单线程用于定时以及周期性执行任务的需求。newWorkStealingPool(1.8
将最旧的任务丢弃,将当前任务添加到队列。
torService(newThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,newLinkedBlockingQueue
容易混淆的参数:corePoolSize maximumPoolSize workQueue
"Hello,World",responseStr);}}配置详解下面我们来详细分析这段代码我们先来看构造函数里的代码,这里的第一部分是创建一个WebHostBuilder,如果了
任务队列、核心线程数、最大线程数的逻辑关系
当线程数小于核心线程数时,创建线程。
当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
当线程数大于等于核心线程数,且任务队列已满
若线程数小于最大线程数,创建线程
若线程数等于最大线程数,调用拒绝执行处理程序(默认效果为:抛出异常,拒绝任务)
那么这三个参数推荐如何设置,有最优值吗?
由于java对于协程的支持不友好,所以会大量依赖于线程池和线程。
从而这个值没有最优推荐,需要根据业务需求情况来进行设置。
不同的需求类型可以创建多个不同的线程池来执行。
.Result来同步阻塞获取异步结果,并且能正确返回真,但是阻塞HttpClient里的异步方法很多时候都会造成死锁.至于原因大家可以查看这篇文章https://blog.stephencleary.
问题1:阿里开发规范为什么不允许Executors快速创建线程池?
可以看到原因很简单
ewTestServer(builder);_client=server.CreateClient();}[Fact]publicasyncTaskSimpleGet(){varresponse=aw
newSingleThreadExecutor
newFixedThreadPool
在 workQueue 参数直接 使用了 new LinkedBlockingQueue() 理论上可以无限添加任务到线程池。
们便可以构建Http请求了.需要说明的是这个HttpClient并没什么特殊的,它就是System.net.http下的httpclient,想必大家多少都用到过.下面我们来看测试方法,测试方法主要就
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue();
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1,
1,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
如果提交到线程池的任务由问题,比如 sleep 永久,会造成内存泄漏,最终导致OOM。
cutor)创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对
同时 阿里还推荐自定义 threadFactory 设置线程名称便于以后排查问题。
依赖于线程池和线程。从而这个值没有最优推荐,需要根据业务需求情况来进行设置。不同的需求类型可以创建多个不同的线程池来执行。问题1:阿里开发规范为什么不允许Executors快速创建线程池?参考地址:h
问题2:下面的代码输出是什么?
应该选C。
虽然最大线程数有100但核心线程数为1,任务队列由100。
满足了 "当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。" 这个条件。
所以后续添加的任务都会被堵塞。
blicclassmvc20{privatereadonlyHttpClient_client;publicmvc20(){varbuilder=newWebHostBuilder().UseCont
最后
关于 ThreadPoolExecutor 的逻辑在实际使用的时候会有点奇怪,因为线程池中的线程并没有超过最大线程数,有没有一种可能当任务被堵塞很久的时候创建新的线程池来处理呢?
一个中间件,要使用必须配置.回到测试方法里,这个startup文件直接指定为mvc项目里的startup文件即可,并且必须要这样,如果测试环境中的startup文件和mvc项目里的不一样测试就显得没有
这边推荐大家使用 newWorkStealingPool,也就是ForkJoinPool。采取了工作窃取的模式。
后续会跟大家一起聊聊 ForkJoinPool。
去运行。这些牵扯到线程池实现的具体策略。还有哪些常见的池?线程池连接池(数据库连接、TCP连接等)BufferPool......Java中的线程池UML图(Java8)可以看到真正的实现类有Thre