多线程高并发学习之线程池从入门到入土
邻近过年,我又开始放荡本人了,托更好几天,明天中午12点半刚回到老家,带着我的狗子,明天是过年之前最初一个集,大巷上可冷落了,我中午吃了饭去外边逛了逛,逛了一圈,想起我曾经鸽了好几天了,于是我良心发现,连忙回到家,关上电脑,开始肝文章。
废话不多说,明天来跟大家讲讲线程池从入门到入土
在讲线程池之前先说几个相干的类
线程池入门前需理解的类
Executor
Executor是一个接口,只有一个execute办法叫执行,也就是说你能够指定一个线程去执行他的run办法
ExecutorService
ExecutorService也是一个接口,它实现了Executor接口,并且又减少了很多的办法,比方shutdownNow马上完结、isShutdown是否完结、submin提交执行(丢给线程池,异步执行)等等比拟罕用的办法
Callable
Callable相当于是Runnable,Callable的call办法相当于Runnable的run办法,惟一不同的是call办法有返回值,而run办法没有返回值
Future
Future是什么呢?Future其实就是下面讲的Callable执行call办法返回的后果,将会被封装到Future外面,ExecutorService的sumin办法,返回值类型就是Future,当调用Future的get办法,将会阻塞住,执行结束并返回
- 在这插一个比拟好玩的Future,叫做FutureTask,它既是一个事件,又是一个后果集,相当于一个有返回值的Runnable与Future的结合体,然而须要new一个Thread去帮他执行,如下图
那这个时候必定有小伙伴们就要问了:啊,那有没有治理多个工作的啊?
听到这,我门头一咒,猛嘬一口凉白开,拍板叫道:当然有!
接下来隆重退场的就是CompletableFuture
这是个啥玩意呢?CompletableFuture有一个动态阻塞办法allOf,能够治理多个工作。
兄弟萌,请看,我写了3个办法,priceofTM、priceofJD、priceofPDD,都是睡不同秒钟,而后让3个CompletableFuture去执行,而后交给一个CompletableFuture去治理,调用这个allOf办法阻塞,直到所有办法执行结束,它还有一个anyOf是当有一个执行结束就解除阻塞
ok开胃菜吃完,当初咱们来干硬菜!MingLog!上硬菜!
线程池从入门到入土
线程池的参数
corePoolSize外围线程数:顾名思义,就是外围线程的个数
maximumPoolSize最大线程数:可能容许存在的线程数(外围线程+非核心线程)
keepAliveTime生存工夫:当非核心线程超过这个工夫没有执行工作,就会被剔除掉,归还给操作系统
TimeUnit生存工夫的单位:工夫单位时、分、秒
workQueue工作队列:各种各样的BlockingQueue
threadFactory线程工厂:产生现成的工厂,须要实现ThreadFactory接口
handler回绝策略:外围线程+非核心线程=最大线程数时,再申请线程时,执行回绝策略
线程池执行流程
当线程池初始化后,其实外面是没有线程的,当要去执行一个工作时,创立一个外围线程去执行,再来一个工作,再去创立一个外围线程
直到创立达到外围线程个数之后再去创立非核心线程时须要查看外围线程有没有闲暇的
如果有闲暇的外围线程,则将这个工作交给外围线程去执行
如果没有闲暇的外围线程(外围线程都在忙),则进入队列等着,等着外围线程忙完
如果期待队列满了,则创立非核心线程去执行工作
如果非核心线程没有执行工作超过keepAliveTime生存工夫,则将这个非核心线程剔除掉交还给操作系统
如果外围线程+非核心线程=最大线程数,并且所有线程都在忙,没有闲暇的,则再来工作须要创立线程时,间接执行回绝策略
这里多bb几句,上述参数有一个期待队列BlockingQueue,罕用的BlockingQueue的实现类不多,通过我种种测试,就有了以下后果,小伙伴们认真看
- 如果队列抉择LinkedBlockingQueue,咱们晓得它是无界队列,能够寄存Integer最大值的工作,所以你的工作队列很有很能会越堆越多,而且只有外围线程在工作,因为只有工作队列满了才会开启非核心线程
- 如果队列抉择ArrayBlockingQueue,这个是比拟失常的,须要给定一个工作队列的长度,当工作沉积到阈值时,开启非核心线程,执行队列外的工作
- 如果队列抉择DelayQueue,上回书说到,这玩意是以工夫进行排序的,所以也是按工作创立的工夫大小来执行的,具体看你重写的compareTo办法,然而!重点来了!这玩意也是个无界的,所以相当于是LinkedBlockingQueue+工夫排序
- 如果队列抉择SynchronusQueue,大家晓得这玩意长度为0,所以,只能是一个个的执行,通过测试,如果你的线程池的线程达到了你设置的阈值,再增加工作时,他就会报错啦,因为只有当前任务被线程取走时能力持续往里增加工作
- 如果队列抉择TransferQueue,成果和LinkedBlockingQueue一毛一样
线程池的回绝策略
如果有人问你,线程池的回绝策略有几种呢?
低情商答复:4种!
高情商答复:JDK默认提供的有4种,然而咱们能够自定义回绝策略!
好家伙,面试官直呼外行呐
JDK默认提供的回绝策略有四种:AbortPolicy、DiscardOldestPolicy、DiscardPolicy、CallerRunsPolicy
- AbortPolicy:简略暴力,间接报错
- DiscardOldestPolicy:间接把队列的第一个工作给干掉,而后把新工作加进来,我间接好家伙,那工作不就丢了吗,慎用慎用
- DiscardPolicy:外表上啥都没干,其实它背地里把你要增加的新工作给偷偷干掉了
- CallerRunsPolicy:将你要增加的这个工作交给main线程去执行
常见的几种线程池
上面给大家介绍几种常见的线程池,面试给面试官说说,面试官直呼外行!
CachedPool
来来来,大家来看他的结构就晓得啦,这玩意没有外围线程,而最大线程数为Integer.MAX_VALUE,存活工夫时60S,队列用的是SynchronousQueue,想想这几个组合在一起是什么成果呢?来一个工作就开启一个线程(前提是线程池里没有闲暇的线程),线程闲暇60S就偿还,周而复始
SingleThreadExecutor
来,在看这个好玩的,乍一看,我敲!一个外围线程,最大线程也是一,那不就是永远只有一个线程嘛,存活工夫0,用的LinkedBlockingQueue作为队列,一个一个排好队来执行
FixedThreadPool
FixedThreadPool,创立的时候给定一个int值作为外围线程数以及最大线程数,嘛意思,就是说池子里全是外围线程,来一个我启动一个,直到达到阈值,而后其余工作进队列等着,等有外围线程忙完再去执行别的工作,排好队,一个一个来
具体我的项目实战中,这个线程数设置多少正当呢?这个货色应该针对我的项目而定
如果你我的项目属于CPU密集型(NIO,只进行一些计算运算,不参加磁盘),那么线程数约等于你服务器的CPU核数
如果你我的项目属于IO密集型(运算少,读写磁盘多),那么线程数设为CPU核数的10倍左右差不多
这货色是有公式的,我遗记了,具体设置好还是要进行压测直到找到最合适的那个线程数,所有以压测数据为准
自定义回绝策略
自定义回绝策略就很灵便了,依据本人的业务来定,我就间接伪代码了
另类的几个线程池
咱们上边说的都是ThreadPoolExecutor类型的,上面介绍几个ForkJoinPool类型的
WorkStealingPool
结构是间接new了一个ForkJoinPool,所以它实质其实就是ForkJoinPool
ForkJoinPool是每个线程都有属于本人的工作队列,每次都去本人的工作队列中取工作,当有的线程把本人队列的工作执行完之后,会去别的工作队列的屁股下来”偷“工作,不讲码德,马保国直呼外行
ForkJoinPool是将大工作进行合成,合成到什么水平呢?直到满足ForkJoinPool自定的那个条件,不满足就持续劈两半合成
好啦,就说这么多,看完了,同学们有没有入土呢,还没入土,就去看看ThreadPoolExecutor的源码,了解的更深,这篇文章肝了3天,不是我low,是回来家把狗子带回来,放楼下怕他乱吃,我不释怀,在我屋里,他老是打搅我,哎,太难了。。。
看在我这么辛苦更新的份上,小伙伴们就不要白嫖了嘛