Java并发编程,关于那些厉害得不得了的线程池...

2018-10-23  本文已影响0人  世说烟雨亦话悲凉

本文编辑于 2018/10/23 不可能侵权,所以不删

ReStartLin


前言

众所周知,在java的后端开发中,少不了和线程打交道,但同时线程的创建和销毁也是很耗费资源的,那么如何提高并发的效率呢? 为了解决这个问题,java就给我们带来了concurrent并发包

前言的前言

既然说到并发,那么中点内容就是线程池了,但是线程池的底层实现又依赖于队列,队列才是对任务进行分发执行的核心...
说到队列,粗略地列表一下就是以下几种:

1.ArrayDeque, (数组双端队列) 
2.PriorityQueue, (优先级队列) 
3.ConcurrentLinkedQueue, (基于链表的并发队列) 
4.DelayQueue, (延期阻塞队列)(阻塞队列实现了BlockingQueue接口) 
5.ArrayBlockingQueue, (基于数组的并发阻塞队列) 
6.LinkedBlockingQueue, (基于链表的FIFO阻塞队列) 
7.LinkedBlockingDeque, (基于链表的FIFO双端阻塞队列) 
8.PriorityBlockingQueue, (带优先级的无界阻塞队列) 
9.SynchronousQueue (并发同步阻塞队列)

还挺多,具体看业务场景选择使用即可.深入了解还需要查找相关内容

线程池

前言说完就说说我们的主角线程池了...
线程池的目的在于:

1、降低资源的消耗,通过重复利用已创建的线程降低线程创建和销毁所造成的消耗
2、提高相应速度
3、提高线程的可管理性

java也给我们便利地带来了对应的实现操作类.

Executor框架
CachedThreadPool:创建一个可缓存线程池,如果线程池的长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程
FixedThreadPool 创建一个定长线程池, 可控制线程最大并发数,超出的线程会在队列中等待
ScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务的执行
SingleThreadExecutor 创建一个单线程化的线程池,他只会用唯一的工作线程来执行任务,保证所有的任务按照指定顺序FIFO LIFO优先级 等 执行

有趣的是,这几个线程池的最顶层实现其实是构造函数的参数不同而已...

Executor 框架的最顶层实现是ThreadPoolExecutor类 Executors工厂类中提供的

ThreadPoolExecutor

这里说说ThreadPoolExecutor构造函数中常用的5个参数,其他还请自行查询资料.

ThreadPoolExecutor(int corePoolSize,
                   int maximumPoolSize,
                   long keepAliveTime,
                   TimeUnit unit,
                   BlockingQueue<Runnable> workQueue) 

一个一个来说明:
corePoolSize:核心线程数,顾名思义 就是线程池中重中之重的线程的数目,被归类在核心线程池中的线程即使没有在使用,也不会被销毁. 当然如果你想它也被销毁复用的话,,,那就设置allowCoreThreadTimeOuttrue
maximumPoolSize:线程池所能容纳的最大线程数。超过这个数的线程将被阻塞。需要注意的是:任务队列为没有设置大小的---LinkedBlockingDeque时,这个值无效。因为LinkedBlockingDeque不设置大小默认是无限大的...所以装都装不完,就不会触发这个参数了.
keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止,配合TimeUnit来使用.
unit:参数 keepAliveTime 的时间单位,有七种取值,在TimeUnit类中有七种静态属性
workQueue:顾名思义是任务队列,取值类型参考上文.

最后一个参数没啥好说的,主要是前面四个参数是配合使用的.

keepAliveTimeunit是用来指定线程在空闲多久时间之后会被销毁用的,那么配合其他的参数一起会是怎么样的呢,,举个例子:

有这么一间工厂,靠生产谋生(这好像是废话_)/. 这家工厂一创立就用了10个员工,假设这家工厂实行永久雇佣制,那么这就是所谓的corePoolSize核心线程了.
继续假设每个员工每次只能着手完成一个任务,那么任务的来源就是流水线了. 那么这条流水线上能放多少个任务呢?这就是靠工作队列workQueue所指定的长度来决定的,下文假设是20;
今天是个好日子,厂里开张得到了20个订单,准备着手生产.
10个员工马不停蹄地领走了10个订单进行生产,剩下的10个订单就只能留在流水线上等待被执行了.

时间流失,又有那么一天,但是这天却很糟糕,厂里不小心接到了50个订单,,,我的天
10个员工拿走10个订单,流水线上放着20个订单,,,,那还剩20个订单进不去呀,,这可怎么办 这样会造成工厂崩溃的
老板灵机一动:拿钱(消耗资源)去请临时员工!! 就这样请来了20个临时员工(线程),这样就解决了燃眉之急了2333

往后几天工作量又平稳了下来,10个员工足以胜任,不能白养着临时员工呀,开掉. 这个临时员工存在的时长呢,就是keepAliveTimeunit

故事说到这里,想必你们都捋清楚了吧, 核心线程就是核心员工,不会被开除(回收),那么maximumPoolSize最大线程数指的就是核心员工加临时员工的数目了,如果任务总量超过了这个值,就会造成系统崩溃的(throws exception).

举个栗子:

ThreadPoolExecutor(1,2,10l,TimeUnit.SECONDE,new LinkedBlockingDeque<String>(5)); 

极限状态下,任务开始执行,核心线程取走1个任务,队列存放5个任务,临时线程为2-1一个,临时创建执行一个任务,那么最大能同时容纳``1+5+1```个任务,超过这个数目就会报错了,,指定临时线程在空闲 10s之后就会被销毁

关于线程数的设置

核心线程数与最大线程数相同.
考虑I/O密集型任务和CPU密集型任务,这两者具体是什么需要查阅相关资料
一般来说,,
CPU密集型任务 一般设置线程数为:CPU核心数或者CPU核心数+1
I/O密集型任务 一般设置线程数为:2* cpu的核数

如果有误,请斧正

上一篇下一篇

猜你喜欢

热点阅读