关于java:多线程高并发学习之线程池从入门到入土

35次阅读

共计 3555 个字符,预计需要花费 9 分钟才能阅读完成。

多线程高并发学习之线程池从入门到入土

邻近过年,我又开始放荡本人了,托更好几天,明天中午 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,是回来家把狗子带回来,放楼下怕他乱吃,我不释怀,在我屋里,他老是打搅我,哎,太难了。。。

看在我这么辛苦更新的份上,小伙伴们就不要白嫖了嘛

正文完
 0